最近有一個項目有這個需求,讓用戶上傳本身的交易憑證的截圖,以前由於對調Android原生的東西不太熟悉,就先放了一邊html
由於項目已經上線,只不過是該功能未開放而已,那麼如今爲何要寫這篇博客呢,是由於........由於最近有客戶產生交易申訴,也就是兩方交易,一個說我給錢了,另外一個說我沒收到錢java
這樣的狀況下,這個功能就顯得很重要了,因而立馬開始着手這個功能的開發android
雖說網上一搜一大把源碼,但是幾乎每個能用的,因而放棄,仍是本身研究吧,這個過程踩過無數坑,掉了無數發,流了無數淚ios
PS:後面會說調IOS相冊的,由於咱們的項目都不可能只有Android或者只有IOS的吧,這裏說一個小插曲,也是這個插曲使我決定了先寫調Android相冊緣由 π_πwindows
我花了兩天時間,看了無數教程,終於把調Android的demo寫好了,而後就開始寫ios 的,又用了兩天把ios的研究好了寫完demo了,回過頭來發現以前Android的demo包又不能用了,真是玄學api
有時候開發這個東西,你不信玄學還真是不行,因而重頭又寫了一遍,仍是不行,因而又開始研究,終於搞定,這篇文章涉及到C#,java,Unity,AndroidStudio,仍是建議有些基礎的人看,好了,下面步入正題app
這篇文章會很長,由於我會把本身踩過的坑詳細的說清楚是怎麼解決的jvm
邏輯:Unity調用Android的方法,打開相冊,選中圖片後Android將圖片路徑做爲參數,調用Unity的方法,而後在Unity里加載圖片出來ide
1、準備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裏面
2、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真機測試
3、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打包編譯的時候也會生成這個文件,和包裏面的衝突了