【手把手】带你撸一个安卓壳子

2023-11-22 01:40
文章标签 手把手 安卓 壳子

本文主要是介绍【手把手】带你撸一个安卓壳子,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

web前端的小伙伴们大家好,说起APP混合开发,大家首先想到的可能就是类似Cordova的库,可以让我们不写一行安卓代码,就轻松地实现原生APP的一些常用功能,但是对于一些特别的"需求"我们就无能为力了。所以为了提高我们的知识储备,我觉得还是有必要学习一波安卓的知识的。下面就由我带着大家手把手撸一个安卓的壳子出来,由于本人也是第一次写安卓的东西,难免会有些不对的地方,希望各路大神见怪莫怪。

1、下载Android Studio

官网下载地址,开发安卓貌似就只有这一个编辑器可用了,这个IDE风格和webStorm的风格一模一样,应该是一家公司的产品,这个没有深究过。安装过程在此就不详细说明了,有需要的可以自行百度。

2、创建一个新项目

打开IDE,点击 Start a new Android Studio project 创建一个新项目 

填写下面的信息(APP名称和域名建议和我填写一样,这样下面就不用改了^_^),点击next

选择APP支持的安卓最低版本,点击next

然后选择一个空白页,点击next

这里直接默认,最后点击 Finish

3、启动项目

至此,我的第一个APP已经创建完成,怎么运行它呢?有两种运行方式,一种是模拟器、一种真机。我建议使用真机,模拟器可能会出现一些意想不到的问题。

选择运行模式

第一个代表模拟器,第二个是真机,这里我们选择真机 USB Device,点击OK

接下来把我们的手机通过USB连接电脑,注意手机要打开 USB调试 ,简单说一下手机怎么打开 USB调试,以华为畅玩9为例,首先我们要打开手机的开发者模式,依次点击 设置>系统>关于手机>连续点击版本号(考验手速的时候到了!)   直到出现 "打开手机开发者模式"字样的提示,然后我们再点击 设置>系统>开发者选项>USB调试开关打开。

然后点击下面的绿色三角,运行程序,开始运行的时候手机上可能会弹出来一次 允许调试的授权框 ,我们点击允许。

大功告成!我们可以看到APP已经成功安装到手机上了

4、修改APP名称和图标

我们可以看到默认的APP图标和名称,作为一名优秀的前端开发,简直不能忍

修改图标:选择 File>New>Image Asset

Name的值不要改,Asset Type 选择 Image,path选择我们的图标路径,再通过 Resize 调整图片到合适大小,点击next。

我们看到了很多红色,不用鸟他,直接Finish

修改名称:打开 app>res>values>strings.xml  修改其中的 app_name 的值

修改完毕,我们再重新运行一下程序,哈哈,非常完美!

5、创建 webView,并去掉标题栏

既然是混合开发,webview是必须的,理论上我们的Android架子只需要一个页面放置webview即可。
首先我们要允许webview联网,打开 app>manifests>AndroidManifest.xml,在 application 上面添加一行:

<uses-permission android:name="android.permission.INTERNET" />

打开  app>res>layout>activity_main.xml 把默认的 TextView 控件删掉,加上webview控件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><WebView  xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/webview"android:layout_width="fill_parent"android:layout_height="fill_parent"/></android.support.constraint.ConstraintLayout>

我们发现还有一个默认的标题,这个也是不需要的,打开 app>manifests>AndroidManifest.xml,找到android:theme="@style/AppTheme"  改成下面的

android:theme="@style/Theme.AppCompat.Light.NoActionBar"

再次运行一下,标题栏没有了,但是白屏,那是因为我们还没有给webview链接。

6、webview的一些基本配置

*设置链接:

我们修改 MainActivity.java 文件中的MainActivity类,如下,(如果某个单词出现红色,说明没有引入包,把鼠标定位到红色单词上,按住 Alt + Enter 按照提示引入相应的包即可,之后不再重复说明此问题。
myWebView.loadUrl( "https://zhengshaoguo.com/static/app/" ); 这行就是webview要加载的地址,我这里放的是一个服务器的地址,当然也可以放本地的,本地怎么放得这里就不做说明了。

public class MainActivity extends AppCompatActivity {private WebView myWebView = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate( savedInstanceState );setContentView( R.layout.activity_main );myWebView = (WebView) findViewById( R.id.webview );WebSettings webSettings = myWebView.getSettings();myWebView.loadUrl( "https://zhengshaoguo.com/static/app/" );}
}

服务器上对应的HTML文件如下:js相信大家都看的懂,就不解释了。

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Carson</title>  <script>function callAndroid(){document.getElementById("output").innerHTML="欢迎来到单身狗联盟";}</script></head><body><button type="button" id="button1" onclick="callAndroid()">test</button><div id="output"></div></body>
</html>

*启用JS:

我们重新运行APP,当点击按钮之后并没有出现文字,这是因为webview默认是禁用JS的,在 MainActivity.java 中
WebSettings webSettings = myWebView.getSettings(); 后面加一句

webSettings.setJavaScriptEnabled( true );

重新运行APP,一切正常。

*让所有的超链接都在webview中打开:

我们再HTML文件中加入一个超链接

<a href='http://www.baidu.com'>点击去百度</a>

重新运行APP,当我们点击 去百度 之后就会提示我们用默认浏览器打开这个网址,

对于混合开发APP,这样肯定是不行的,我们要让所有的链接都在webview中打开,在 MainActivity.java 中
webSettings.setJavaScriptEnabled( true ); 后面上如下内容

myWebView.setWebViewClient(new WebViewClient() {public boolean shouldOverrideUrlLoading(WebView view, String url) {return false;}
});

*允许webview拨打电话:

前端的小伙伴们都知道,给一个a标签的href属性设置为 tel:18538328225 ,在微信中,点击这个标签就可以调起手机通讯录,便于快捷拨号,那我们在我们的APP中试一下。结果却很尴尬。居然毫无反应,不能忍,改!修改 setWebViewClient() 方法如下:

myWebView.setWebViewClient(new WebViewClient() {public boolean shouldOverrideUrlLoading(WebView view, String url) {if (url.startsWith("tel:")) {Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));startActivity(intent);return true;}return false;}
});

*开启Storage:

sessionStorage和localStorage在很多项目里都会用到,特别是移动端,webview默认是不允许js访问的需要我们手动开启
在 setWebViewClient() 方法下面加上下面内容

webSettings.setDomStorageEnabled( true );
webSettings.setAppCacheMaxSize( 1024 * 1024 * 8 );
String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();
webSettings.setAppCachePath( appCachePath );
webSettings.setAllowFileAccess( true );
webSettings.setAppCacheEnabled( true );

*返回键返回webview的历史记录:

当我们点击 去百度 就在本页面就打开了百度的链接,但当我们点击手机的物理返回键后,并没有返回我们的首页,而是直接退出了程序。这个也需要我们去做处理,在 onCreate() 的后面添加

public boolean
onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {myWebView.goBack();//返回上个页面return true;}return super.onKeyDown(keyCode, event);//退出H5界面
}

7、允许 input type:file 选择文件

这一块的内容还是比较多,也不是必须要开启的功能,所以单独拿出来说,在上传图片的时候,我们会用input标签 type=file 就能实现选择文件,并上传的功能,同样的,这也需要webview提供支持,在 private WebView myWebView = null; 后面加上  

private ValueCallback<Uri[]> uploadMessageAboveL;

然后在  setWebViewClient() 后面添加

webSettings.setAllowFileAccess(true);
myWebView.setWebChromeClient( new WebChromeClient(){public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams ) {if (uploadMessageAboveL != null) {uploadMessageAboveL.onReceiveValue(null);uploadMessageAboveL = null;}uploadMessageAboveL = filePathCallback;Intent i = new Intent(Intent.ACTION_GET_CONTENT);i.addCategory(Intent.CATEGORY_OPENABLE);i.setType("image/*");startActivityForResult( Intent.createChooser(i, "Image Chooser"), 2);return true;}
});

最后在 onKeyDown() 后面加上

protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult( requestCode, resultCode, data );if (requestCode == 2) {if (null == uploadMessageAboveL) return;Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();if (uploadMessageAboveL != null) {onActivityResultAboveL(requestCode, resultCode, data);}}
}//webview可以选择文件
private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");if (requestCode != 2 || uploadMessageAboveL == null) {return;}Uri[] results = null;if (resultCode == Activity.RESULT_OK) {if (data == null) {} else {String dataString = data.getDataString();ClipData clipData = data.getClipData();if (clipData != null) {results = new Uri[clipData.getItemCount()];for (int i = 0; i < clipData.getItemCount(); i++) {ClipData.Item item = clipData.getItemAt(i);results[i] = item.getUri();}}if (dataString != null) {results = new Uri[]{Uri.parse(dataString)};}}}uploadMessageAboveL.onReceiveValue(results);uploadMessageAboveL = null;return;
}

完美收工!至此一个完整的 webview 已经完成。谢谢阅读!

...

...

...

...

...

陈大海:你这个也太low了,还不如去用cordova,根本不用自己配置!
 

em....,看来只好放大招了!

8、JS 和 Android 实现交互

js和android实现交互有很多种方式,这里我选择了最简单的一种,以供大家快速上手。
首先我们把 Android 的类对象(AndroidtoJs)和JS(test)的对象建立映射关系,在 myWebView.loadUrl() 上面加一行

myWebView.addJavascriptInterface( new AndroidtoJs(), "test" );

然后在 onKeyDown()  下面添加与JS映射的类并添加一个hello方法

public class AndroidtoJs extends Object {@JavascriptInterfacepublic void hello(String msg) {final String content = msg;myWebView.post( new Runnable() {@RequiresApi(api = Build.VERSION_CODES.KITKAT)@Overridepublic void run() {myWebView.evaluateJavascript( "javascript:test.helloFn('hello,"+content+"')", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) { }} );}} );}
}

编辑我们的JS文件

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>Carson</title><script>localStorage.setItem("text", "欢迎来到单身狗联盟");function callAndroid() {document.getElementById("output").innerHTML = localStorage.getItem("text");}function sayHello() {test.hello(document.getElementById("name").value);}test.helloFn = function (text) {document.getElementById("output").innerHTML = text;}</script>
</head>
<body><button type="button" id="button1" onclick="callAndroid()">test</button><div id="output"></div><a href='http://www.baidu.com'>点击去百度</a><a href='tel:18668168404'>打电话</a><input type="file"><input id="name" type="text"><button onclick="sayHello()">say</button>
</body>
</html>

效果如下:

简单解释一下,因为 JS的 test 对象和Android的 AndroidtoJs 类进行了映射关系,所以当我用 js  调用 test 的 hello 方法,就相当于android 的 AndroidtoJs 去调用 hello 方法一样,反过来也是一样的。
既然js和android已经实现了交互,那么我们就来搞一波事情吧。

9、获取当前的实时GPS位置

首先我们定义一个方法,实现和JS的交互,在 AndroidtoJs 类里添加

//获取当前地理位置
@JavascriptInterface
public void getPosition(String msg) {showGPSContacts();
}

然后我们在 AndroidManifest.xml 文件中加上权限

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

最后在 MainActivity.java 文件中的 onActivityResult() 方法后面加上

//APP授权的回调
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if(requestCode == 100){if (grantResults[0] == PERMISSION_GRANTED && grantResults.length > 0) {getLocation();} else {showGPSContacts();}}
}//获取地理位置
LocationManager lm;
Boolean positionNum=false;
public void showGPSContacts() {lm = (LocationManager) MainActivity.this.getSystemService(MainActivity.this.LOCATION_SERVICE);boolean ok = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);if (ok) {if (Build.VERSION.SDK_INT >= 23) {if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PERMISSION_GRANTED) {  // 没有权限,申请权限。ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE}, 100);} else {getLocation();}} else {getLocation();}} else {Intent intent = new Intent();intent.setAction(Settings.ACTION_LOCATION_SOURCE_SETTINGS);startActivityForResult(intent, 1315);}
}
private void getLocation() {LocationManager locationManager;String serviceName = Context.LOCATION_SERVICE;locationManager = (LocationManager) this.getSystemService(serviceName);Criteria criteria = new Criteria();criteria.setAccuracy(Criteria.ACCURACY_FINE);criteria.setAltitudeRequired(false);criteria.setBearingRequired(false);criteria.setCostAllowed(true);criteria.setPowerRequirement(Criteria.POWER_LOW);String provider = locationManager.getBestProvider(criteria, true);if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PERMISSION_GRANTED) { return; }Location location = locationManager.getLastKnownLocation(provider); // 通过GPS获取位置updateLocation(location);if(!positionNum){positionNum=true;lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 8, new LocationListener() {@Overridepublic void onLocationChanged(Location location) {updateLocation(location);}@Overridepublic void onStatusChanged(String provider, int status, Bundle extras) {}@SuppressLint("MissingPermission")@Overridepublic void onProviderEnabled(String provider) {updateLocation(lm.getLastKnownLocation(provider));}@Overridepublic void onProviderDisabled(String provider) {updateLocation(null);}});}
}
private void updateLocation(Location location) {if (location != null) {final double latitude = location.getLatitude();final double longitude = location.getLongitude();System.out.println(latitude+":"+longitude);myWebView.post( new Runnable() {@RequiresApi(api = Build.VERSION_CODES.KITKAT)@Overridepublic void run() {myWebView.evaluateJavascript( "javascript:test.getPositionFn('" + latitude+"','"+longitude+ "')", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) { }} );}} );} else {System.out.println("无法获取到位置信息");}
}

修改JS文件

test.getPosition('');
test.getPositionFn=function(lat,lng){document.getElementById("output").innerHTML = "lat:"+lat+",lng:"+lng;
}

重新运行APP查看效果

说明,首先会去判断手机有没有打开GPS,没有打开跳到手机系统打开GPS的页面,打开了GPS的,再去向用户请求获取地理位置的权限,等用户授权之后,每两秒查询一次用户的位置,如果有更新,就调用 js 的方法,把最新的经纬度传过去。

10、实现扫描二维码

这里我们使用一个第三方的包来实现此功能

引入包文件,打开下面文件

修改  其中的 allprojects 如下

allprojects {repositories {google()jcenter()maven { url 'https://jitpack.io' }}
}

 打开下面文件

在 dependencies 中加入

implementation 'com.github.yuzhiqiang1993:zxing:2.2.1'

注意修改这两个文件之后窗口的顶部会出现如下提示,需要我们点击安装

然后我们在 AndroidManifest.xml 文件中加上权限

<uses-permission android:name="android.permission.CAMERA" />

 AndroidtoJs 类里定义一个和JS交互的方法

//启动扫描二维码
@JavascriptInterface
public void scanQRCode(String msg) {if (ContextCompat.checkSelfPermission( MainActivity.this, android.Manifest.permission.CAMERA ) != PERMISSION_GRANTED) {ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.CAMERA}, 1 );} else {Intent intent = new Intent( MainActivity.this, CaptureActivity.class );startActivityForResult( intent, 111 );}
}

在 onRequestPermissionsResult() 中加一个打开摄像头的授权方法

//APP授权的回调
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if(requestCode == 100){if (grantResults[0] == PERMISSION_GRANTED && grantResults.length > 0) {getLocation();} else {showGPSContacts();}}else if(requestCode == 1){if(grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED){Intent intent = new Intent( MainActivity.this, CaptureActivity.class );startActivityForResult( intent, 111 );}}
}

最后在 onActivityResult() 方法里加上扫描二维码的结果

protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult( requestCode, resultCode, data );if (requestCode == 2) {if (null == uploadMessageAboveL) return;Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();if (uploadMessageAboveL != null) {onActivityResultAboveL(requestCode, resultCode, data);}}else if (requestCode == 111 && resultCode == RESULT_OK) {if (data != null) {final String content = data.getStringExtra( Constant.CODED_CONTENT );myWebView.post( new Runnable() {@RequiresApi(api = Build.VERSION_CODES.KITKAT)@Overridepublic void run() {myWebView.evaluateJavascript( "javascript:test.scanQRCodeFn('" + content + "')", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) { }} );}} );}}
}

在修改一下JS的调用

function qrCode(){test.scanQRCode('');
}
test.scanQRCodeFn=function(text){document.getElementById("qrCode").innerHTML = "扫描二维码的结果是:"+text;
}

最后再重新运行一下

已经连续奋战3个小时了,今天就到这里了,存个档,有需要的朋友可以点这里下载全部代码

这篇关于【手把手】带你撸一个安卓壳子的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/406566

相关文章

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法

消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法   消除安卓SDK更新时的“https://dl-ssl.google.com refused”异常的方法 [转载]原地址:http://blog.csdn.net/x605940745/article/details/17911115 消除SDK更新时的“

安卓玩机工具------小米工具箱扩展工具 小米机型功能拓展

小米工具箱扩展版                     小米工具箱扩展版 iO_Box_Mi_Ext是由@晨钟酱开发的一款适用于小米(MIUI)、多亲(2、2Pro)、多看(多看电纸书)的多功能工具箱。该工具所有功能均可以免root实现,使用前,请打开开发者选项中的“USB调试”  功能特点 【小米工具箱】 1:冻结MIUI全家桶,隐藏状态栏图标,修改下拉通知栏图块数量;冻结

安卓开发板_联发科MTK开发评估套件串口调试

串口调试 如果正在进行lk(little kernel ) 或内核开发,USB 串口适配器( USB 转串口 TTL 适配器的简称)对于检查系统启动日志非常有用,特别是在没有图形桌面显示的情况下。 1.选购适配器 常用的许多 USB 转串口的适配器,按芯片来分,有以下几种: CH340PL2303CP2104FT232 一般来说,采用 CH340 芯片的适配器,性能比较稳定,价

安卓实现弹出软键盘屏幕自适应调整

今天,我通过尝试诸多方法,最终实现了软键盘弹出屏幕的自适应。      其实,一开始我想通过EditText的事件来实现,后来发现,安卓自带的函数十分强大,只需几行代码,便可实现。实现如下:     在Manifest中设置activity的属性:android:windowSoftInputMode="adjustUnspecified|stateHidden|adjustResi

【视频教程】手把手AppWizard轻松制作一个emWin滑动主界面控制框架,任意跳转控制(2024-09-06)

现在的新版AppWizard已经比较好用,用户可以轻松的创建各种项目常规界面。 比如早期创建一个支持滑动的主界面框架,并且可以跳转各种子界面,仅仅界面布局和各种图片格式转换都要花不少时间,而现在使用AppWizard,可以说轻轻松松,毫不费力。 用户唯一要做的就是根据自己的芯片性能做一定的速度优化。 视频: https://www.bilibili.com/video/BV17Rp3eLE

大模型的学习路线(非常详细)神仙级教程,手把手教会你

如果读者朋友不想深入学习大模型,则了解提示词的使用原则也可以了。要是既不想深入学习,又要做大模型相关的项目,则对于工程同学来说,学习RAG也能把大模型玩转起来(可参考:[大语言模型RAG落地方案]。下面的步骤写给想系统性学习大模型的朋友们。(后续打算写一个大模型学习系列,详细介绍相关知识点,欢迎关注) 先来一张整体结构图,越是下面部分,越是基础: 可以按以下步骤学习: 1. 理解基础概念

安卓错误经验分析之 R cannot be resolved to a variable

当出现 R cannot be resolved to a variable  错误的时候,不能采用编译器建议的修改方法,试着clean一下,然后查找gen文件夹下R.java是否丢失,如果不存在R.java,程序没有报错且采用其它方法均无效,八成是res文件夹下的layout或者manifest出现错误没有显示出来,需要自己查一遍,否则无法根本解决问题,盲目修改代码是没用的。

【红日靶场】ATTCK实战系列——红队实战(一)手把手教程

目录 入侵网络的思路 一些概念 (1)工作组 (2)域 (3)账号 红日靶机(一) 网络结构 下载 配置web服务器的两张网卡 配置内网的两台机器(域控和域内主机) 渗透web服务器 外网信息搜集 (1)外网信息搜集的内容 (2)开始信息搜集(主要是利用工具) 漏洞利用 (1)漏洞利用的两种方式 (2)利用phpMyAdmin (3)开启3389端口远程桌面

[已更新问题一代码]2024数学建模国赛高教社杯A题:“板凳龙” 闹元宵 思路代码文章助攻手把手保姆级

问题1 问题描述 舞龙队沿着螺距为55 cm的等距螺旋线顺时针盘入。龙头前把手的速度保持在1m/s,初始位置在螺旋线的第16 圈(点A,坐标为 (880 cm, 0))。螺旋线的终点为 (0, 0)。任务是计算从0秒到300秒间,每秒龙头、龙身、龙 尾的每个前把手和龙尾后把手的中心位置和速度,并将结果保存到文件 result1 .xlsx 中。 解决思路 螺旋线的参数化方程 螺旋线是从外向