分类目录归档:Android

iOS/Android的WebView中用file input支持拍照或选择相册的照片

如果我们的一个移动端的网页需要让用户上传一张照片,那么,通常而言,我们可以写了以下一段HTML代码

<input type='file' />

那么Mobile Web界面将会显示下面的一个控件,通过该控件,用户可以拍照或者选择手机中的文件而上传。

在iOS下,Mobile Safari会在你点击上面的控件之后弹出如下界面
Screen Shot 2017-03-12 at 下午6.44.31

在Android系统下,弹出的界面根据具体的Android版本(不同的厂商定制版本、不同的Android系统版本)不同而略有不同,不过,通常也会提供相机,相册等选项。下图为小米手机的例子。
IMG_20170312_184934

然而,如果你在原生的应用中内嵌了一个UIWebView/WKWebView(iOS)或者WebView(Android),你就会发现,原来在Mobile Safari和Chrome中可以正常运行的代码很有可能不能正常工作了。

在iOS系统下,你会发现,如果UIWebView所在的View Controller是通过presentViewController展示的,那么,你会发现,用户点击 后,你的iOS应用会出现一些奇怪的问题,如应用崩溃,如你在相册选择了照片之后,UIWebView所在的View Controller不见了,等等。

最后,我的解决方法是,不通过presentViewController展示UIWebView所在的View Controller,而是通过UINavigationController的pushViewController去展示View Controller。

而对于Android系统,应用内嵌的WebView本来就不支持使用文件上传功能,所以,<input type=’file’ />在Mobile Chrome上有效,到了应用内嵌的WebView中就无效了。

在Android上,我的解决方法是这样的,通过WebView的addJavascriptInterface注入一个camera对象,让WebView中的js代码通过camera对象从而唤起相机或者相册。

webView.addJavascriptInterface(new DemoJavaScriptInterface(), “demo”);

Javascript代码

window.demo.camera();

假设DemoJavaScriptInterface的是位于WebViewActivity里面的一个内部类,且WebViewActivity有一个私有变量String mCameraPhotoPath,那么,DemoJavaScriptInterface代码如下

 final class DemoJavaScriptInterface {
        DemoJavaScriptInterface() {
        }

        @JavascriptInterface
        public void camera() {

                Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
                    // Create the File where the photo should go
                    File photoFile = null;
                    try {
                        photoFile = createImageFile();
                        takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
                    } catch (Exception ex) {
                        // Error occurred while creating the File
                        Log.e("WebViewSetting", "Unable to create Image File", ex);
                    }

                    // Continue only if the File was successfully created
                    if (photoFile != null) {
                        mCameraPhotoPath = photoFile.getAbsolutePath();
                        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                                Uri.fromFile(photoFile));
                    } else {
                        takePictureIntent = null;
                    }
                }

                Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
                contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
                contentSelectionIntent.setType("image/*");

                Intent[] intentArray;
                if (takePictureIntent != null) {
                    intentArray = new Intent[]{takePictureIntent};
                } else {
                    intentArray = new Intent[0];
                }

                chooserIntent = new Intent(Intent.ACTION_CHOOSER);
                chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
                chooserIntent.putExtra(Intent.EXTRA_TITLE, "拍照或者选择图片");
                chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

                Activity mContext = WebViewActivity.this;
                if (Build.VERSION.SDK_INT >= 23) {
                    int checkCallPhonePermission = ContextCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA);
                    if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) {
                        ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_ASK_CAMERA);
                        return;
                    }
                }

                WebViewActivity.this.startActivityForResult(chooserIntent, 101);

            }

    }

WebViewActivity中的onActivityResult方法代码如下

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                case 101:
                    if(data == null){
                        String imageurl = mCameraPhotoPath;
                        //相机拍好的照片就保存在路径imageurl中
                    }
                    else{
                        Uri uri = data.getData();
                        //通过uri获取照片数据
                    }

                    break;

                default:
                    return;
            }
        }

    }

解决问题:React Native Error building DependencyGraph: Error: Naming collision detected

React Native是Facebook出品的一款支持iOS和Android的跨平台开发框架,可以让工程师用reactjs编写iOS和Android应用,不过在使用过程中,我们都会遇到一些问题。

如 https://github.com/facebook/react-native/issues/3440 里所说的一样,如果你再一个React Native工程中还是用了cocoapods,那么你就可能遇到Pods/目录下和工程当前目录下都有node_modules,从而导致以下错误

Error building DependencyGraph:
Error: Naming collision detected: /Users/dev/Documents/Xcode/react-native/AwesomeProject/Pods/React/Libraries/vendor/react/platformImplementations/universal/worker/UniversalWorkerNodeHandle.js collides with /Users/dev/Documents/Xcode/react-native/AwesomeProject/node_modules/react-native/Libraries/vendor/react/platformImplementations/universal/worker/UniversalWorkerNodeHandle.js
at HasteMap._updateHasteMap (HasteMap.js:123:13)
at HasteMap.js:95:28
at tryCallOne (/Users/jimmy/Documents/Xcode/react-native/AwesomeProject/node_modules/react-native/node_modules/promise/lib/core.js:37:12)
at /Users/jimmy/Documents/Xcode/react-native/AwesomeProject/node_modules/react-native/node_modules/promise/lib/core.js:103:15
at flush (/Users/jimmy/Documents/Xcode/react-native/AwesomeProject/node_modules/react-native/node_modules/promise/node_modules/asap/raw.js:50:29)
at doNTCallback0 (node.js:417:9)
at process._tickCallback (node.js:346:13)

 

怎么办呢?目前知道的有个办法

定制projectRoots

在工程目录下执行命令 ./node_modules/react-native/packager/packager.sh –projectRoots `pwd`/src –projectRoots `pwd`/node_modules

 

用gradle编译包含了nativelib(so文件)的Android工程

2014年7月,我进行了一个跨部门的项目,该项目包含iOS和Android版本,iOS版本很快就弄好了,但是Android版本却在配置管理上让我走了些弯路。

之前,我辅助开发的大多数Android项目都是用Maven管理的,这次却遇到了Gradle工程。
最开始遇到的问题是,我通过homebrew安装了1.12的gradle,结果Idea提示,请使用1.10版本的gradle,我想了想,算了,还是下一个1.10的吧。
接下来,倒是很顺利,编译通过,并且成功在手机和模拟器上运行,可是,恩,adb logcat中居然发现了UnsatisfiedLink!根据我的经验,这显然就是native lib没有打包进去的原因啊。把apk打开看,果然,所有的so文件都没有打包进去。
怎么办呢?到stackoverflow和gradle的网站上去查找,有各种的讨论,最后的解决方法却很简单。
工程中native lib的目录结构是
libs
 |- x86
 |- armeabi
 | – a.so
 | – b.so
最后我把libs/*.so和libs/x86/*.so libs/armeabi/*.so打包到一个叫做native.jar文件中,jar文件内部的目录结构如下
lib   // (注意,lib,不带s)
 |- x86
 |- armeabi
 | – a.so
 | – b.so
然后,把native.jar放到libs目录下。
最后,gradle编译,运行,模拟器和手机上一切正常了

Intel HAXM导致x86的android emulator模拟器运行在OSX 10.9 mavericks上,mac就死机

2013年12月的时候,当时还用的OS X 10.9,用Mac开发android的时候遇到了一个烦人的问题,装上了HAXM,intel hardware accelerated execution manager以后,一运行android emulator x86,mac就死机了,无论按键盘还是移动鼠标,mac都不反应,只能长按电源键关机再开机。

刚开始还以为是意外,没想到重新启动mac后,再运行一次emulator,我的mac还是死机了。反复几次都如此。
最后,终于找到了
http://software.intel.com/forums/topic/477793
里面提到
 The hotfix is available for download! Please go to http://software.intel.com/en-us/articles/intel-hardware-accelerated-execution-manager/ . There is a hotfix for Microsoft Windows* 8.1 and one for OS X 10.9. 
看来,原来是HAXM还不支持太新的操作系统的缘故啊,Windows 8.1和OSX 10.9都是这个原因。
我装上了这个hot fix以后,x86的emulator终于正常了,osx也不会死机了。