Unity內部安裝更新App三種方案(安卓APk)

歡迎加入Unity業內qq交流羣:956187480

qq掃描二維碼加羣


方案1:

因爲公司U3D項目在立項之初都沒有能做好熱更新的規化,導致現在要去做U3D的熱更新非常難,並且項目已處於中後期,大部分的方案不管是用反射(DLL反射更新),還是用Lua,都需要把項目大部分代碼結構推倒重來。於是就放棄熱更新,選擇還是直接用Apk來更新遊戲。

在開始之前先是在網上查了很多資料,但是能夠讓我等初級開發者能夠順手就能用的基本沒有,再加上我們很多Unity開發者對Android和Ios的原生開發都不瞭解,就導致上手困難。今天在這裏儘量詳細記錄。

1.apk上傳和下載

將工程中StreamingAssets這個目錄下的文件全部清空,然後再用Unity打包一個APK。把APK文件上傳至服務器,用www下載到指定路徑即可,至於爲啥要清空StreamingAssets,是因爲我的項目資源是用ab包加載的,在第一次安裝的時候都已經釋放的本地了,如果更新版本沒有資源更新就清空即可,如果有資源更新就不能清空。正常應該做資源ab包的熱更新,這個有時間再給大家分享。

public IEnumerator InstallApk()
    {
        WWW www = new WWW("http://192.168.10.100/down/android/demo/demo.apk");
        //下載需要更新的apk
        while (true)
        {
            Debug.Log(www.progress / 1f * 100);
            if (www.isDone)
            {
                break;
            }
            yield return null;
        }
        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.Log("error:" + www.error);
            yield return 0;
        }
            //將apk寫入沙盒目錄
            string path = Application.persistentDataPath + "/test.apk";
            File.WriteAllBytes(path, www.bytes);
    }

拷貝APK安裝路徑Application.dataPath的asset目錄文件到可讀寫目錄Application.persistentDataPath

下載到指定路徑後就需要在程序內部調用安裝。需要跟android交互調用原生方法。

2.Unity跟Android交互

參考之前的博客:Unity調用安卓原生的通用前奏(血淚史)

3.核心代碼

安卓端:判斷安卓版本7.0是分水嶺

   public void InstallApk(String apkPath) {
        System.out.println("Android下載地址:"+apkPath);
        File file = new File(apkPath);
        Intent intent = new Intent(Intent.ACTION_VIEW);

        if(Build.VERSION.SDK_INT>=24) { //Android 7.0及以上
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            // 參數2 清單文件中provider節點裏面的authorities ; 參數3  共享的文件,即apk包的file類
            Uri apkUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID+".fileprovider", file);//記住修改包名
            //對目標應用臨時授權該Uri所代表的文件
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        }else{
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        this.startActivity(intent);
    }

Unity端:上面已經下載更新包到指定路徑了直接調用安裝就行

   using (AndroidJavaClass cl = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
            {
                using (AndroidJavaObject ob = cl.GetStatic<AndroidJavaObject>("currentActivity"))
                {
                    //然後調用android來安裝apk
                    ob.Call("InstallApk", path);
                }
            }

這個安裝會覆蓋掉之前安裝的APK,由於這個APK裏asset目錄是空的,所以覆蓋之後也就沒有asset了,這也是爲什麼要做第一步的原因。另外,這個Application.persistentDataPath+filename 在Java端調用的時候,需要在路徑最前面添加”file://”。這裏強調一下,重新安裝APK後,Application.persistentDataPath和WWW.LoadFromCacheOrDownload緩存的文件,是不會被覆蓋的,所以請放心覆蓋原來的APK。

4.注意

有個權限問題:三步走

最後在安卓端需要加個V4依賴,我會把v4包也在工程裏給到大家。添加依賴的方法很簡單。另外因爲我安卓的目標版本是8.1,所以上面那個7.0的邏輯判斷我沒有驗證7.0一下的需要大家注意。博客記錄起來很容易,但是在真正的操作中會遇到很多各種各樣的問題,特別是android的api版本還有Tool版本跟unity項目的衝突問題。只有當你遇到了你才能體會,不過話說回來當你都給解決掉的時候就說明水平上升了。


方案2:

因爲是 vr項目,工程內部接入的第三方的sdk已經整合過安卓,如果按照以上方案下載的話因爲MainActivity的衝突在vr一體機息屏再次亮屏的話就會黑屏。原因是兩個Mainactivity加載順序導致的。後來選擇另外一種方案,弱化跟安卓的交互,直接在unity內調用下載。

1.代碼:

public void InstallAPK(string path)
    {
        try
        {
            var FileProvider = new AndroidJavaClass("android.support.v4.content.FileProvider");
            var Intent = new AndroidJavaClass("android.content.Intent");
            var ACTION_VIEW = Intent.GetStatic<string>("ACTION_VIEW");
            var FLAG_ACTIVITY_NEW_TASK = Intent.GetStatic<int>("FLAG_ACTIVITY_NEW_TASK");
            var FLAG_GRANT_READ_URI_PERMISSION = Intent.GetStatic<int>("FLAG_GRANT_READ_URI_PERMISSION");
            var intent = new AndroidJavaObject("android.content.Intent", ACTION_VIEW);
            var UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
            var currentActivity = UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

            var file = new AndroidJavaObject("java.io.File", path);
            var uri = FileProvider.CallStatic<AndroidJavaObject>("getUriForFile", currentActivity, "包名.fileprovider", file);
            intent.Call<AndroidJavaObject>("setFlags", FLAG_ACTIVITY_NEW_TASK);
            intent.Call<AndroidJavaObject>("addFlags", FLAG_GRANT_READ_URI_PERMISSION);
            intent.Call<AndroidJavaObject>("setDataAndType", uri, "application/vnd.android.package-archive");

            currentActivity.Call("startActivity", intent);
        }
        catch (System.Exception e)
        {
            Debug.LogError(e.Message);
        }
    }

 2.清單文件添加:

 <provider
             android:name="android.support.v4.content.FileProvider"
             android:authorities="包名.fileprovider"
             android:grantUriPermissions="true"
             android:exported="false">
      <meta-data 
        android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/filepaths" />
    </provider>

3. 新建xml文件,參考方案1,可以從安卓拖進unity,也可以直接在unity創建


方案3:

應該是還有一種方案,直接在安卓端創建新的activity不繼承unityactivity的。然後打jar或者arr包在unity內部直接調用。

參考:https://blog.csdn.net/u013341672/article/details/80266116,沒看太懂,測試了也無效

安卓端代碼:

public class Battery {
    public static void Oninit(Object s){
        Context context;
        context = (Context) s;
        Intent intent =new Intent();
        intent.putExtra("my","123");
        intent.setClass(context,com.pico.Integration.MyActivity.class);
        context.startActivity(intent);
    }
}
public class MyActivity extends Activity {
    public void InstallApk(String apkPath) {
        System.out.println("Android下載地址:"+apkPath);
        File file = new File(apkPath);
        Intent intent = new Intent(Intent.ACTION_VIEW);

        if(Build.VERSION.SDK_INT>=24) { //Android 7.0及以上
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            // 參數2 清單文件中provider節點裏面的authorities ; 參數3  共享的文件,即apk包的file類
            Uri apkUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID+".fileprovider", file);//記住修改包名
            //對目標應用臨時授權該Uri所代表的文件
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        }else{
            intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        this.startActivity(intent);
    }
}

 Unity端代碼:調用應該是有問題的Oninit方法可以執行,但是MyActivity裏面的InstallApk方法調不到,後面就沒有再研究,大家可以深入一下

  AndroidJavaObject ob = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
            AndroidJavaObject ajo = new AndroidJavaClass("包名.Battery");

            ajo.CallStatic("Oninit", ob);
           
           // ajo.Call("InstallApk", path);
            ob.Call("InstallApk", path);

歡迎加入Unity業內qq交流羣:956187480

qq掃描二維碼加羣

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章