Unity調用Android相冊


最近有一個項目有這個需求,讓用戶上傳自己的交易憑證的截圖,之前因為對調Android原生的東西不太熟悉,就先放了一邊

因為項目已經上線,只不過是該功能未開放而已,那么現在為什么要寫這篇博客呢,是因為........因為最近有客戶產生交易申訴,也就是兩方交易,一個說我給錢了,另一個說我沒收到錢

這樣的情況下,這個功能就顯得很重要了,於是立馬開始着手這個功能的開發

雖然說網上一搜一大把源碼,可是幾乎每一個能用的,於是放棄,還是自己研究吧,這個過程踩過無數坑,掉了無數發,流了無數淚

 

PS:后面會說調IOS相冊的,因為我們的項目都不可能只有Android或者只有IOS的吧,這里說一個小插曲,也是這個插曲使我決定了先寫調Android相冊原因 π_π

我花了兩天時間,看了無數教程,終於把調Android的demo寫好了,然后就開始寫ios 的,又用了兩天把ios的研究好了寫完demo了,回過頭來發現之前Android的demo包又不能用了,真是玄學

有時候開發這個東西,你不信玄學還真是不行,於是重頭又寫了一遍,還是不行,於是又開始研究,終於搞定,這篇文章涉及到C#,java,Unity,AndroidStudio,還是建議有些基礎的人看,好了,下面步入正題

這篇文章會很長,因為我會把自己踩過的坑詳細的說清楚是怎么解決的

邏輯:Unity調用Android的方法,打開相冊,選中圖片后Android將圖片路徑作為參數,調用Unity的方法,然后在Unity里加載圖片出來

一、准備jar包,供Unity使用

Unity和Android交互,這個jar包就是一個中間商,也就是說,我們獲取相冊里的相片路徑,並告知Unity這個路徑,這一部分功能在安卓端實現

(1)androidstudio新建工程,這一系列沒有什么好說的,新建一個空工程,值得注意是下圖圈起來的兩個地方,packagename==>這個必須要和你的Unity工程的包名一樣,

不然打出來的包安裝完成后會閃退的,還有就是最低支持的API等級,這個也必須和Unity里面的buildsetting里面的對應

 

 

 (2)接下來在MainAcitivy里面復制我下面的代碼,這里要注意在復制代碼的時候,第一行是包名,這里被我刪了,因為每個人包名都不一樣,包名需要保留,不能沒有

之后你會發現TakePhoto是灰色的,不用管它,這個Unity調用android的入口,這個函數名要是改了話,Unity的C#代碼也需要改對應的,不然調不到

然后你還會發現WebViewActivity和UnityPlayerActivity報錯,先別慌,穩住,看下一步!

 2 import android.content.Context;
 3 import android.content.Intent;
 4 import android.os.Bundle;
 5 import android.util.Log;
 6 import com.unity3d.player.UnityPlayerActivity;
 7 
 8 public class MainActivity extends UnityPlayerActivity{
 9     private static String LOG_TAG = "LOG_My";
10     Context mContext = null;
11 
12     @Override
13     public void onCreate(Bundle savedInstanceState) {
14         super.onCreate(savedInstanceState);
15         mContext = this;
16     }
17 
18     //Unity中會調用這個方法,用於打開本地相冊
19     public void TakePhoto(String str)
20     {
21         Log.d(LOG_TAG,str);
22         Intent intent = new Intent(mContext,WebViewActivity.class);
23         this.startActivity(intent);
24     }
25 
26 }

 

(3)導入Unity的classes.jar

先說這個文件在哪,在你所使用的Unity的安裝目錄里面,這里需要注意呀,不是什么版本的Unity的這個classes.jar都能用的,必須是你Unity工程所使用的版本的安裝目錄

 

 

 將這個jar包賦值一份,並拷貝到剛才新建的Android項目的app\libs文件夾里點擊左上角的Android圖片,切換到project視圖就能找到這個文件夾

 

 

 然后右鍵這個classes.jar,選擇add as library,會彈出一個框,確定是給app添加的就行,確定后等待編譯一會,UnityPlayerActivity就會正常了,這是因為Android需要和Unity通信,就必須得調用Unity的類才行

(4)創建WebViewActivity類,切換回Android視圖找到我們的MainActivty,這個都會在app\java\第一個包名的文件夾下

然后右鍵,new一個javaclass,命名就是WebViewActivity,創建的時候什么都不用管,都是默認就行

 

 

 然后你就會看到什么報錯都沒了,接下來把下面的代碼復制到WebViewActivity,這里還是一樣不能自己的包名給粘貼調哈

 3 import android.app.Activity;
 4 import android.content.ContentResolver;
 5 import android.content.Intent;
 6 import android.database.Cursor;
 7 import android.graphics.Bitmap;
 8 import android.net.Uri;
 9 import android.os.Bundle;
10 import android.provider.MediaStore;
11 import android.util.Log;
12 import android.view.KeyEvent;
13 
14 import com.unity3d.player.UnityPlayer;
15 
16 import java.io.IOException;
17 
18 
19 public class WebViewActivity extends Activity
20 {
21     public static final int NONE = 0;
22 
23     public static final int PHOTORESOULT = 3;
24 
25     public static final String IMAGE_UNSPECIFIED = "image/*";
26 
27 
28 
29     private String LOG_TAG = "LOG_ZDQ";
30     @Override
31     protected void onCreate(Bundle savedInstanceState) {
32 
33         super.onCreate(savedInstanceState);
34 
35         Intent intent = new Intent(Intent.ACTION_PICK, null);
36         intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_UNSPECIFIED);
37         startActivityForResult(intent, PHOTORESOULT);
38         Log.d(LOG_TAG, "打開相冊!");
39     }
40 
41     @Override
42     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
43         Log.d(LOG_TAG, "resultCode :" + resultCode);
44         if (resultCode == NONE)
45             return;
46 
47         if (data == null)
48             return;
49 
50         
51         ContentResolver resolver = getContentResolver();
52         Bitmap bm=null;
53         
54         Uri originalUri = data.getData();
55         try {
56             bm = MediaStore.Images.Media.getBitmap(resolver, originalUri);
57         } catch (IOException e) {
58             e.printStackTrace();
59         }
60 
61 
62         String[] proj = {MediaStore.Images.Media.DATA};
63 
64         65         
66         Cursor cursor= getContentResolver().query(originalUri,proj,null,null,null);

68         int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
6
70         cursor.moveToFirst();
71
72         String _path = cursor.getString(column_index);
73 
74         UnityPlayer.UnitySendMessage("Main Camera", "GetPhoto", _path);

76         super.onActivityResult(requestCode, resultCode, data);
77
78         this.finish();
79     }
80 }

這里需要注意的地方

 

 

 這是調用Unity的main camera 上的腳本里的Getphoto方法,path是傳遞過去的參數

unity里面需要在什么地方加這個功能,以及腳本掛載什么對象身上,這些都要先考慮好之后才能打這個jar包,到這里第一階段Android的功能已經寫完了,接下來就是打包了,坑較多,一定要注意了

(5)准備打jar包,新建module  file - new - new module - Android Library - next 這里的packagename和最小版本sdk也要設置一下和上面的一樣

然后之前寫的兩個腳本復制到你新建的module的同樣的位置,然后切換到project視圖,將app\libs里面的classes.jar也復制到new module的libs里面,然后一樣的add as library,一樣的注意跟Unity一樣的包名

 

 

 

 (6)編輯new module的Androidmanifest.xml,復制一下代碼粘貼就行,注意包名是自己的,在這里加相冊權限,包名和unity的不一樣的話,到時候unity打出來apk會閃退

之后打完包之后 這個xml文件需要和打出來的jar包一起放到Unity工程里面的

 1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 2     package="com.NXNC.Farm" >
 3     <!--讀取相冊權限 (必須)-->
 4     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 5     <application   >
 6         <activity android:name=".MainActivity">
 7             <intent-filter>
 8                 <action android:name="android.intent.action.MAIN" />
 9                 <category android:name="android.intent.category.LAUNCHER" />
10             </intent-filter>
11         </activity>
12         <activity
13             android:name=".WebViewActivity">
14         </activity>
15     </application>
16 
17 </manifest>

(7)在build.gradle中加上一下代碼,這里注意,這個build.gradle是你新建module的build.gradle

task makejar(type: Copy){

    delete 'libs/test.jar'

    from('build/intermediates/packaged-classes/release/')

    into('libs')

    include('classes.jar')

    rename('classes.jar','FarmGetAndroidPhoto.jar')
}

makejar.dependsOn(build)

這里的坑就是from,有不同的位置,但是基本都是這個目錄,如果你最后發現你打包成功之后,卻發現沒有包,就是這個地方錯了

(8)打jar包,這里也是坑最多的地方,網上的解決方法也是五花八門

打包之前建議:

先在Androidstudio的安裝目錄的bin文件夾下找到studio.exe.vmoptions或者studio64.exe.vmoptions,看你是32位還是64的,就編輯哪個,按如下編輯

 

 gradle.properties注釋掉org.gradle.jvmrgs這句話,也許值會不一樣,反正注釋掉就行

 

 然后就可以打包了

在Terminal里面輸入命令 gradlew makejar  等一會就會提示build successful  這就是包打好了

左上角切換回project視圖,在新建module的libs里面就會有新的jar,這個就是我們需要的

最后我們需要的兩個東西  就是這個打出來jar和new module的AndroidManifest文件,復制到Unity里面

 

二、Unity調用

(1)unity工程asset文件下新建文件夾plugins-Android,然后AndroidManifest和jar包放入就行

奉上demo腳本

 1 using UnityEngine;
 2 using System.IO;
 3 using UnityEngine.UI;
 4 
 5 public class GetAndroidPhoto : MonoBehaviour
 6 {
 7     public Button button;
 8     public RawImage rawImage;
 9 
10     void Start ()
11     {
12         button.onClick.AddListener(OpenLibery);
13     }
14 
15     private void OpenLibery()
16     {
17         AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
18         AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
19         jo.Call("TakePhoto", Application.persistentDataPath);
20     }
21 
22     public void GetPhoto(string path)
23     {
24         Debug.Log("android give path ==> " + path);
25         FileGetTex(path);
26     }
27 
28     private void FileGetTex(string path)
29     {
30         FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
31         fileStream.Seek(0, SeekOrigin.Begin);
32         byte[] bye = new byte[fileStream.Length];
33         fileStream.Read(bye, 0, bye.Length);
34         fileStream.Close();
35 
36         Texture2D texture2D = new Texture2D((int)rawImage.rectTransform.rect.width, (int)rawImage.rectTransform.rect.height);
37         texture2D.LoadImage(bye);
38         rawImage.texture = texture2D;
39     }
40 }

TakePhoto、GetPhoto分別和Android工程里的對應的,這里還有一個很玄學的問題,我使用的加載圖片試過了www,試過了http,就是不行,最后只能通過文件流的方式加載

寫完demo是不能直接在unity運行的,要打出apk真機測試

 

三、Unity gradle模式下打包apk

(1)檢查buildsetting - playersetting - other setting的packagename是否與AndroidManifest里面的一直,不一樣的就恭喜自己,要么unity改包名,要么改Android工程,從頭再來

(2)檢查build setting的build system,一定要是Gradle,因為我們需要與Android交互,就必須選這個,這里埋個坑,就是我們打好的jar現在還不能直接放到unity里打測試包的,會報錯的

但是你選internal這個包就能順利打出來,但是這樣放到正式工程中你就不能簽名了,所以當你打包出錯,並報錯

 

 

CommandInvokationFailure: Gradle build failed.
C:/Program Files (x86)/Java/jdk1.8.0_73\bin\java.exe -classpath "C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib\gradle-launcher-4.2.1.jar" org.gradle.launcher.GradleMain "-Dorg.gradle.jvmargs=-Xmx1024m" "assembleRelease"

stderr[
Dex: Error converting bytecode to dex:
Cause: com.android.dex.DexException: Multiple dex files define Lcom/NXNC/Farm/BuildConfig;
UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dex.DexException: Multiple dex files define Lcom/NXNC/Farm/BuildConfig;
at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:660)
at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:615)
at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:597)
at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:170)
at com.android.dx.merge.DexMerger.merge(DexMerger.java:197)
at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:503)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:333)
at com.android.dx.command.dexer.Main.runDx(Main.java:288)
at com.android.dx.command.dexer.Main.main(Main.java:244)
at com.android.dx.command.Main.main(Main.java:95)


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':transformDexWithDexForRelease'.
> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException: Error while executing java process with main class com.android.dx.command.Main with arguments {--dex --num-threads=4 --output E:\MX\Project\Unity\TestFarmPhotoFinally\Temp\gradleOut\build\intermediates\transforms\dex\release\0 --min-sdk-version 16 E:\MX\Project\Unity\TestFarmPhotoFinally\Temp\gradleOut\build\intermediates\transforms\preDex\release\0.jar E:\MX\Project\Unity\TestFarmPhotoFinally\Temp\gradleOut\build\intermediates\transforms\preDex\release\3.jar E:\MX\Project\Unity\TestFarmPhotoFinally\Temp\gradleOut\build\intermediates\transforms\preDex\release\2.jar}

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org

BUILD FAILED in 11s
]
stdout[
Starting a Gradle Daemon (subsequent builds will be faster)
Observed package id 'tools' in inconsistent location 'E:\SDK\android-sdk-windows2017-12-25 (1)\android-sdk-windows2017-12-25\toolsOLD' (Expected 'E:\SDK\android-sdk-windows2017-12-25 (1)\android-sdk-windows2017-12-25\tools')
Already observed package id 'tools' in 'E:\SDK\android-sdk-windows2017-12-25 (1)\android-sdk-windows2017-12-25\tools'. Skipping duplicate at 'E:\SDK\android-sdk-windows2017-12-25 (1)\android-sdk-windows2017-12-25\toolsOLD'
Observed package id 'tools' in inconsistent location 'E:\SDK\android-sdk-windows2017-12-25 (1)\android-sdk-windows2017-12-25\toolsOLD' (Expected 'E:\SDK\android-sdk-windows2017-12-25 (1)\android-sdk-windows2017-12-25\tools')
Already observed package id 'tools' in 'E:\SDK\android-sdk-windows2017-12-25 (1)\android-sdk-windows2017-12-25\tools'. Skipping duplicate at 'E:\SDK\android-sdk-windows2017-12-25 (1)\android-sdk-windows2017-12-25\toolsOLD'
:preBuild UP-TO-DATE
:preReleaseBuild UP-TO-DATE
:compileReleaseAidl UP-TO-DATE
:compileReleaseRenderscript UP-TO-DATE
:checkReleaseManifest UP-TO-DATE
:generateReleaseBuildConfig UP-TO-DATE
:prepareLintJar UP-TO-DATE
:generateReleaseResValues UP-TO-DATE
:generateReleaseResources UP-TO-DATE
:mergeReleaseResources UP-TO-DATE
:createReleaseCompatibleScreenManifests UP-TO-DATE
:processReleaseManifest
:splitsDiscoveryTaskRelease UP-TO-DATE
:processReleaseResources
:generateReleaseSources
:javaPreCompileRelease
:compileReleaseJavaWithJavac UP-TO-DATE
:compileReleaseNdk NO-SOURCE
:compileReleaseSources UP-TO-DATE
:lintVitalRelease
:mergeReleaseShaders UP-TO-DATE
:compileReleaseShaders UP-TO-DATE
:generateReleaseAssets UP-TO-DATE
:mergeReleaseAssets
:transformClassesWithPreDexForRelease
:transformDexWithDexForRelease

Running dex as a separate process.

To run dex in process, the Gradle daemon needs a larger heap.
It currently has 1024 MB.
For faster builds, increase the maximum heap size for the Gradle daemon to at least 1536 MB.
To do this set org.gradle.jvmargs=-Xmx1536M in the project gradle.properties.
For more information see https://docs.gradle.org/current/userguide/build_environment.html

:transformDexWithDexForRelease FAILED
20 actionable tasks: 7 executed, 13 up-to-date
]
exit code: 1
UnityEditor.Android.Command.WaitForProgramToRun (UnityEditor.Utils.Program p, UnityEditor.Android.Command+WaitingForProcessToExit waitingForProcessToExit, System.String errorMsg) (at <fdd3823527dc4dde8316302bb5a76d3b>:0)
UnityEditor.Android.Command.Run (System.Diagnostics.ProcessStartInfo psi, UnityEditor.Android.Command+WaitingForProcessToExit waitingForProcessToExit, System.String errorMsg) (at <fdd3823527dc4dde8316302bb5a76d3b>:0)
UnityEditor.Android.AndroidJavaTools.RunJava (System.String args, System.String workingdir, System.Action`1[T] progress, System.String error) (at <fdd3823527dc4dde8316302bb5a76d3b>:0)
UnityEditor.Android.GradleWrapper.Run (UnityEditor.Android.AndroidJavaTools javaTools, System.String workingdir, System.String task, System.Action`1[T] progress) (at <fdd3823527dc4dde8316302bb5a76d3b>:0)
Rethrow as GradleInvokationException: Gradle build failed
UnityEditor.Android.GradleWrapper.Run (UnityEditor.Android.AndroidJavaTools javaTools, System.String workingdir, System.String task, System.Action`1[T] progress) (at <fdd3823527dc4dde8316302bb5a76d3b>:0)
UnityEditor.Android.PostProcessor.Tasks.BuildGradleProject.Execute (UnityEditor.Android.PostProcessor.PostProcessorContext context) (at <fdd3823527dc4dde8316302bb5a76d3b>:0)
UnityEditor.Android.PostProcessor.PostProcessRunner.RunAllTasks (UnityEditor.Android.PostProcessor.PostProcessorContext context) (at <fdd3823527dc4dde8316302bb5a76d3b>:0)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

 

 

網上會推薦你講gradle 換成 internal ,這個建議純屬一派胡言,這並不是解決問題,而是繞過了問題,堅決不能采取這個!!!!!!!

會說你的Android工程的內存分配不夠 更會說jdk sdk ndk版本不對,也許你試過一大圈之后發現並沒有用,我在這里卡了整整兩天,最后......

我的解決方案:

用壓縮包打開我們的jar,然后刪掉里面的buildconfig.class,最后順利解決,gradle模式下打包成功,demo在手機上成功運行

這是因為Unity打包編譯的時候也會生成這個文件,和包里面的沖突了

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM