安卓webApp开发模板
最近团队需要做一个安卓app,使用原生的android开发的话,开发周期会比较长,考虑到时间因素团队成员考虑采用webapp的形式来开发,这样门槛会相对来说也会低一些。
开发工具:
-
Android Studio
具体思路如下:
同时这里采用了快捷菜单来设置请求的URL,预计效果如下(安卓系统得在7.1以上才支持):
总的来说这样的开发门槛很低,对于零基础的安卓开发人员也很容易上手,具体的代码也不难,主要是安卓的布局是xml格式的,对于习惯了html的朋友来说,初始会感到不习惯。
接下来我们聊一下具体的代码实现细节:
整体项目层次结构如下:
配置文件:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mywebviewapp" android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:networkSecurityConfig="@xml/network_security_config"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!--add static shortcut注意把这个都加到主页面上,至于下面这个类 我们在xml/static_shortcut下面已经配置了/-->
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/static_shortcut" />
</activity>
<!-- 注意这里有结果active就得注册几个,不注册的话 如果要跳转到该active则会出现闪退现象 -->
<activity android:name=".WebSettingActivity">
</activity>
</application>
</manifest>
请注意:
使用webview一定要给联网权限
<uses-permission android:name="android.permission.INTERNET" />
配置静态快捷菜单
<!--add static shortcut注意把这个都加到主页面上,至于下面这个类 我们在xml/static_shortcut下面已经配置了/-->
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/static_shortcut" />
静态快捷键菜单配置文件xml/static_shortcut.xml
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<!--设置UrL-->
<shortcut
android:enabled="true"
android:icon="@drawable/ic_shortcut_search"
android:shortcutDisabledMessage="@string/lable_shortcut_static_search_disable"
android:shortcutId="shortcut_id_search"
android:shortcutLongLabel="@string/lable_shortcut_static_search_long"
android:shortcutShortLabel="@string/lable_shortcut_static_search_short">
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.example.mywebviewapp.WebSettingActivity"
android:targetPackage="com.example.mywebviewapp" />
<!--当前只有这个分类-->
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>
主布局页:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="false"
android:layout_centerHorizontal="true"
android:layout_marginHorizontal="8dp" />
</LinearLayout>
设置请求URL:activity_web_setting.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
tools:context=".WebSettingActivity" >
<Button
android:id="@+id/btnCancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:onClick="myClickBtnBack"
android:text="返回" />
<Button
android:id="@+id/btnSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/btnCancel"
android:layout_alignBottom="@+id/btnCancel"
android:layout_alignParentRight="true"
android:onClick="myClickBtnSave"
android:text="保存" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/btnCancel"
android:layout_below="@+id/btnCancel"
android:layout_marginTop="29dp"
android:text="Url:"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
android:id="@+id/txtUrl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/textView1"
android:layout_alignBottom="@+id/textView1"
android:layout_alignRight="@+id/btnSave"
android:layout_toRightOf="@+id/textView1"
android:ems="10" >
<requestFocus />
</EditText>
</RelativeLayout>
主入口类:MainActivity.class:
package com.example.mywebviewapp;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.KeyEvent;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import java.util.concurrent.ExecutionException;
public class MainActivity extends Activity {
private WebView webView;
private ProgressDialog progressBar;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取webview
webView = (WebView) findViewById(R.id.webView1);
// 获取触摸焦点
webView.requestFocus();
//设置webview禁止缓存 方便调试
webView.getSettings().setCacheMode(webView.getSettings().LOAD_NO_CACHE);
// 取消滚动条
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
// 构建缩放控制
webView.getSettings().setBuiltInZoomControls(false);
// 设置支持缩放
webView.getSettings().setSupportZoom(false);
//设置webview支持JS
webView.getSettings().setJavaScriptEnabled(true);
/*
* 用webview打开指定网页
* 网址取保存在SharedPreferences的参数
*/
progressBar = new ProgressDialog(this);
progressBar.setMessage("正在加载中......");
progressBar.setCancelable(true);
webView.setWebViewClient(
new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
//等待进度条
public void onPageStarted(WebView view, String url, Bitmap favicon) {
//Log.i(TAG, "Started loading URL: " +url);
//progressBar = ProgressDialog.show(MainActivity.this, "title", "message",true);
progressBar.show();
}
public void onPageFinished(WebView view, String url) {
//Log.i(TAG, "Finished loading URL: " +url);
if (progressBar.isShowing()) {
progressBar.dismiss();
}
}
});
SharedPreferences userInfo = getSharedPreferences("user_info", 0);
if (userInfo.getString("url", "").toString().equals("")) {
//页面跳转至WebSettingActivity
Intent intent = new Intent(MainActivity.this, WebSettingActivity.class);
startActivity(intent);
return;
}
try {
if (!new Connection().execute(userInfo.getString("url", "").toString()).get()) {
linkWebSettingActive();
return;
}
} catch (ExecutionException e) {
linkWebSettingActive();
e.printStackTrace();
} catch (InterruptedException e) {
linkWebSettingActive();
e.printStackTrace();
}
//布局中设置的方向是horizontal,设置成LayoutParams(0,heignt,weight) 即width需要设置权重就在width处为0
//布局中设置的方向是vertical,设置成LayoutParams(width,0,weight) 即heignt需要设置权重就在heignt处为0
//webView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
try {
webView.loadUrl(userInfo.getString("url", "").toString());
}
catch (Exception e)
{
e.printStackTrace();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return false;
}
private void linkWebSettingActive() {
//构建一个Toast,相当于网页的alert。 makeText(Context 上下文对象,String字符串,显示延迟的时间);
Intent intent = new Intent(MainActivity.this, WebSettingActivity.class);
startActivity(intent);
Toast ts = Toast.makeText(MainActivity.this, "当前请求链接不可达,请重新输入新的请求链接!", Toast.LENGTH_LONG);
ts.show();
}
}
由于安卓默认在主线程内不允许发生其他请求(会提示NetworkOnMainThreadException),所以要想检查URL是否可用,我们需要异步非阻塞的方式来进行,这样我们的安卓app响应也会更快。
增加一个connection.class:
package com.example.mywebviewapp;
import android.os.AsyncTask;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
public class Connection extends AsyncTask<String, Boolean, Boolean> {
private boolean checkUrlIsAvailable(String urlStr) {
try {
HttpsURLConnection httpsCon=null;
HttpURLConnection httpCon=null;
URL url = new URL(urlStr);
if(urlStr.contains("https"))
{
httpsCon = (HttpsURLConnection) url.openConnection();
int code = httpsCon.getResponseCode();
if (code != 200) {
httpsCon.disconnect();
return false;
}
httpsCon.disconnect();
}
else
{
httpCon = (HttpURLConnection) url.openConnection();
int code = httpCon.getResponseCode();
if (code != 200) {
httpCon.disconnect();
return false;
}
httpCon.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected Boolean doInBackground(String... urls) {
return checkUrlIsAvailable(urls[0]);
}
}
设置动态链接:WebSettingActivity.class
package com.example.mywebviewapp;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.view.View;
import android.widget.EditText;
public class WebSettingActivity extends Activity {
private EditText txtUrl = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_setting);
txtUrl = (EditText) findViewById(R.id.txtUrl);
SharedPreferences userInfo = getSharedPreferences("user_info", 0);
txtUrl.setText(userInfo.getString("url", ""));
}
/*
* 保存按钮事件
*/
public void myClickBtnSave(View v) {
/*
* 将表单结果保存到SharedPreferences中
*/
SharedPreferences userInfo = getSharedPreferences("user_info", 0);
userInfo.edit().putString("url", txtUrl.getText().toString()).commit();
Intent intent = new Intent(WebSettingActivity.this, MainActivity.class);
startActivity(intent);
}
/*
* 返回按钮事件
*/
public void myClickBtnBack(View v) {
finish(); //只是推向后台
}
}
解决http链接默认不能访问的问题network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!--Set application-wide security config using base-config tag.-->
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>
这里我用的安卓模拟器是:夜神模拟器,为什么使用它呢,主要是觉得android studio中的模拟器太重(当然这是个人拙见),另外如果可以的话,我们可以直接在真机上来debug我们的webapp,这需要开启设备的【USB调试】,不同设备的开启方式不同。
具体调试方式可以参考:谷歌远程调试的文档(可能需要翻墙)
参考文章:
App Shortcuts的官方文档: App Shortcuts