这篇文章主要围绕weex-android开发问题和androidwear开发展开,旨在为您提供一份详细的参考资料。我们将全面介绍weex-android开发问题的优缺点,解答androidwear开发
这篇文章主要围绕weex-android 开发问题和android wear开发展开,旨在为您提供一份详细的参考资料。我们将全面介绍weex-android 开发问题的优缺点,解答android wear开发的相关问题,同时也会为您带来Android beta APK分发问题、android EditText的android:gravity="right"和android:hint="开源"两属性不能同时生效的有关问题、android google map 开发问题、Android Weex 填坑记的实用方法。
本文目录一览:- weex-android 开发问题(android wear开发)
- Android beta APK分发问题
- android EditText的android:gravity="right"和android:hint="开源"两属性不能同时生效的有关问题
- android google map 开发问题
- Android Weex 填坑记
weex-android 开发问题(android wear开发)
weex 打包android不能加载本地图片且不支持base64
官方文档虽然有说
Schemes
本地资源
Weex SDK 提供 local scheme 来访问打包在应用程序中的资源,此 scheme 无法在 H5 环境下使用。目前,开发者可以在 image 组件和字体文件中使用本地资源。
在 iOS 中,Weex 会在 bundle resources 中查找。例如,image 组件的 src 属性为 local:///app_icon'', Weex 会加载 bundle resouce 中名为 app_icon 的图像资源,而字体文件也以相同的方式工作。
在 Android 中,image 组件将从 drawable 资源文件夹加载,如 res/drawable-xxx。但加载字体文件是不同的,Android 框架无法从 res 加载字体文件,因此 SDK 将从 asserts 文件夹加载它。
但使用local加载图片时在ios下可以成功加载图片base64,android不能加载
解决方案:
用Android Studio打开.. ... weex-project-test/platforms/android,
修改... .../platforms/android/app/src/main/java/com/weex/app/extend/ImageAdapter.java
将图片放在... .../platforms/android/app/src/main/assets/images


package com.weex.app.extend;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Base64;
import android.widget.ImageView;
import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.WXSDKManager;
import com.taobao.weex.adapter.IWXImgLoaderAdapter;
import com.taobao.weex.common.WXImageStrategy;
import com.taobao.weex.dom.WXImageQuality;
public class ImageAdapter implements IWXImgLoaderAdapter {
public ImageAdapter() {
}
public Bitmap stringtoBitmap(String string){
//将字符串转换成Bitmap类型
Bitmap bitmap=null;
try {
byte[]bitmapArray;
bitmapArray= Base64.decode(string, Base64.DEFAULT);
bitmap= BitmapFactory.decodeByteArray(bitmapArray, 0, bitmapArray.length);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
@Override
public void setImage(final String url, final ImageView view,
WXImageQuality quality, final WXImageStrategy strategy) {
WXSDKManager.getInstance().postOnUiThread(new Runnable() {
@Override
public void run() {
if(view==null||view.getLayoutParams()==null){
return;
}
if (TextUtils.isEmpty(url)) {
view.setImageBitmap(null);
return;
}
String temp = url;
// base64 start
if (url.startsWith("data")) {
int index = url.indexOf(":");
String tempUrl = url.substring(index + 1,url.length());
Bitmap bitmap = stringtoBitmap(tempUrl);
view.setImageBitmap(bitmap);
return;
}
// base64 end
if (url.startsWith("//")) {
temp = "http:" + url;
}
// 加载本地图片 start
if(url.startsWith("assets")){
int index = url.indexOf(":");
String tempUrl = url.substring(index + 1,url.length());
temp = "file:///android_asset/images/"+tempUrl;
}
// 加载本地图片 end
if (view.getLayoutParams().width <= 0 || view.getLayoutParams().height <= 0) {
return;
}
if(!TextUtils.isEmpty(strategy.placeHolder)){
Picasso.Builder builder=new Picasso.Builder(WXEnvironment.getApplication());
Picasso picasso=builder.build();
picasso.load(Uri.parse(strategy.placeHolder)).into(view);
view.setTag(strategy.placeHolder.hashCode(),picasso);
}
Picasso.with(WXEnvironment.getApplication())
.load(temp)
.transform(new BlurTransformation(strategy.blurRadius))
.into(view, new Callback() {
@Override
public void onSuccess() {
if(strategy.getImageListener()!=null){
strategy.getImageListener().onImageFinish(url,view,true,null);
}
if(!TextUtils.isEmpty(strategy.placeHolder)){
((Picasso) view.getTag(strategy.placeHolder.hashCode())).cancelRequest(view);
}
}
@Override
public void onError() {
if(strategy.getImageListener()!=null){
strategy.getImageListener().onImageFinish(url,view,false,null);
}
}
});
}
},0);
}
}
tips: 使用weex框架中npm run android 和genymotion调试android时看不到效果,npm run pack:android打包后即可
<template>
<image style="width:550px;height:296px" :src="loginbg"></image>
<image style="width:550px;height:296px" :src="''data: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''"></image>
</template>
<script>
... ...
created () {
let platform = weex.config.env.platform.toLowerCase();
if (platform === ''android'') {
this.loginbg = ''assets:loginbg.png'';
} else if (platform === ''ios'') {
this.loginbg = ''local://images/loginbg.png'';
}
},
... ..
</script>
android打包信息修改
... ... /platforms/android/build.gradle
android 字体文件
直接将字体文件拷到... .../platforms/android/app/src/main/assets 即可,引用方式同下ios引用字体代码片段
tips: 每次都要拷贝文件麻烦,可在package.json中添加命令在打包和开发时拷贝文件到该目录下,如
"copy:android": "mkdir -p platforms/android/app/src/main/assets/; cp -rf src/assets/fonts platforms/android/app/src/main/assets/; cp -rf src/assets/images platforms/android/app/src/main/assets/",
"pack:android": "npm run copy:android && npm run clean:android && weex build android",
"android": "npm run copy:android && weex run android",
报错:
Process: com.yiducloud.misdiag, PID: 2457
java.lang.RuntimeException: Unable to instantiate application com.weex.app.WXApplication: java.lang.ClassNotFoundException: Didn''t find class "com.weex.app.WXApplication" on path: DexPathList[[zip file "/data/app/com.yiducloud.misdiag-2/base.apk"],nativeLibraryDirectories=[/data/app/com.yiducloud.misdiag-2/lib/x86, /data/app/com.yiducloud.misdiag-2/base.apk!/lib/x86, /system/lib, /vendor/lib]]
at android.app.LoadedApk.makeApplication(LoadedApk.java:802)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5335)
at android.app.ActivityThread.-wrap2(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1528)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
解决:参考https://www.aliyun.com/zixun/wenji/1224263.html
Android的项目目录里是有两个build文件夹的,一个是:项目目录/app/build,另一个是:项目目录/build。
把两个build都删掉,然后重新编译运行即可。
注意:在提交代码的时候两个build是不应该提交的。
问题: 安卓webview中的字体会随着系统字体改变大小
在../platforms/android/sdk/src/main/java/com/taobao/weex/ui/view/WXWebView.java 添加代码,
// 防止安卓手机放大webview中的字体
settings.setTextZoom(100);
问题:please select andriod sdk
android-webview cookie 同步问题
weex-android默认没有做http请求同步cookie到webview中,参考网上实现代码,如下:
这里是将weex的android的sdk下载并拷贝到了工程中,需要在.../platforms/android/app/build.gradle中配置
../platforms/android/settings.gradle中配置
include '':app'', '':sdk''
在../platforms/android/sdk/src/main/java/com/taobao/weex/ui/view/WXWebView.java


/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.taobao.weex.ui.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.JavascriptInterface;
import android.webkit.JsPromptResult;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.taobao.weex.WXSDKEngine;
import com.taobao.weex.adapter.RouterTracker;
import com.taobao.weex.appfram.storage.IWXStorageAdapter;
import com.taobao.weex.utils.WXLogUtils;
import com.taobao.weex.utils.YiduCommonUtil;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class WXWebView implements IWebView {
private Context mContext;
private String mOrigin;
private WebView mWebView;
private ProgressBar mProgressBar;
private boolean mShowLoading = true;
private Handler mMessageHandler;
private static final int POST_MESSAGE = 1;
private static final String BRIDGE_NAME = "__WEEX_WEB_VIEW_BRIDGE";
private static final int SDK_VERSION = Build.VERSION.SDK_INT;
// downgraded by CVE-2012-6636(https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6636)
private static final boolean DOWNGRADE_JS_INTERFACE = SDK_VERSION < 17;
private OnErrorListener mOnErrorListener;
private OnPageListener mOnPageListener;
private OnMessageListener mOnMessageListener;
public WXWebView(Context context, String origin) {
WXLogUtils.v("test", "WXWebView-construct");
mContext = context;
mOrigin = origin;
}
@Override
public View getView() {
FrameLayout root = new FrameLayout(mContext);
root.setBackgroundColor(Color.WHITE);
mWebView = new WebView(mContext);//mContext.getApplicationContext();
FrameLayout.LayoutParams wvLayoutParams =
new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
wvLayoutParams.gravity = Gravity.CENTER;
mWebView.setLayoutParams(wvLayoutParams);
root.addView(mWebView);
initWebView(mWebView);
mProgressBar = new ProgressBar(mContext);
showProgressBar(false);
FrameLayout.LayoutParams pLayoutParams =
new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
mProgressBar.setLayoutParams(pLayoutParams);
pLayoutParams.gravity = Gravity.CENTER;
root.addView(mProgressBar);
mMessageHandler = new MessageHandler(this);
return root;
}
@Override
public void destroy() {
if (getWebView() != null) {
getWebView().removeAllViews();
getWebView().destroy();
mWebView = null;
}
}
@Override
public void loadUrl(String url) {
if(getWebView() == null)
return;
getWebView().loadUrl(url);
}
@Override
public void loadDataWithBaseURL(String source) {
if (getWebView() == null) {
return;
}
getWebView().loadDataWithBaseURL(mOrigin, source, "text/html", "utf-8", null);
}
@Override
public void reload() {
if(getWebView() == null)
return;
getWebView().reload();
}
@Override
public void goBack() {
if(getWebView() == null)
return;
getWebView().goBack();
}
@Override
public void goForward() {
if(getWebView() == null)
return;
getWebView().goForward();
}
/*@Override
public void setVisibility(int visibility) {
if (mRootView != null) {
mRootView.setVisibility(visibility);
}
}*/
@Override
public void postMessage(Object msg) {
if (getWebView() == null) return;
try {
JSONObject initData = new JSONObject();
initData.put("type", "message");
initData.put("data", msg);
initData.put("origin", mOrigin);
evaluateJS("javascript:(function () {"
+ "var initData = " + initData.toString() + ";"
+ "try {"
+ "var event = new MessageEvent(''message'', initData);"
+ "window.dispatchEvent(event);"
+ "} catch (e) {}"
+ "})();");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
@Override
public void setShowLoading(boolean shown) {
mShowLoading = shown;
}
@Override
public void setOnErrorListener(OnErrorListener listener) {
mOnErrorListener = listener;
}
@Override
public void setOnPageListener(OnPageListener listener) {
mOnPageListener = listener;
}
@Override
public void setOnMessageListener(OnMessageListener listener) {
mOnMessageListener = listener;
}
private void showProgressBar(boolean shown) {
if (mShowLoading) {
mProgressBar.setVisibility(shown ? View.VISIBLE : View.GONE);
}
}
private void showWebView(boolean shown) {
mWebView.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
}
private @Nullable WebView getWebView() {
//TODO: remove this, duplicate with getView semantically.
return mWebView;
}
// 同步 cookie
public void synCookies(final Context context) {
try {
CookieSyncManager.createInstance(context);//创建一个cookie管理器
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.removeSessionCookie();// 移除以前的cookie
cookieManager.removeAllCookie();
WXLogUtils.v("test", "移除以前的cookie");
IWXStorageAdapter adapter = WXSDKEngine.getIWXStorageAdapter();
if (adapter == null) {
return;
}
// 获取weexStorage中cookie
/***
* storageCookie是自定义的用于标识存储cookie的key值,需要在weex代码中存入,如:
* 其中Domain、path的设置比较关键
* let storageCookie = [
* ''SERAWEREWFSDFA;Path=/; HttpOnly'',
* ''ASDFWEASEWEASE;Max-Age=86400; Domain=.cnblogs.com; Path=/; HttpOnly''
* ]
* storage.setItem(''storageCookie'', JSON.stringify({"cookie": storageCookie, "url": [对应要同步cookie的域名]}));
*/
adapter.getItem("storageCookie", new IWXStorageAdapter.OnResultReceivedListener() {
@Override
public void onReceived(Map<String, Object> data) {
if (TextUtils.equals("success", data.get("result").toString())) {
String objectStr= data.get("data").toString();
JSONObject jsonObject=JSONObject.parseObject(objectStr);
String tmpCookie = jsonObject.getString("cookie");
JSONArray cookies = JSONObject.parseArray(tmpCookie);
String url = jsonObject.getString("url");
WXLogUtils.v("test", "拿到storageCookie-----" + cookies.toString() + "------" +url);
if (cookies == null || cookies.size() == 0 || url == null || url.equals("")) { // 没有拿到url和cookie,直接退出
return;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {// 5.0以下
CookieSyncManager.createInstance(context);
CookieSyncManager.getInstance().startSync();
}
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
for (int i = 0; i < cookies.size(); i++) {
String cookie = cookies.getString(i);
cookieManager.setCookie(url, cookie);
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {// 5.0以下
CookieSyncManager.getInstance().sync();
} else { // 5.0以上 刷新cookie
CookieManager.getInstance().flush();
}
String newCookie = cookieManager.getCookie(url);
WXLogUtils.v("test", "设置后的--cookie---" + newCookie + "------");
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void initWebView(WebView wv) {
WebSettings settings = wv.getSettings();
settings.setAllowFileAccess(true);
settings.setJavaScriptEnabled(true);
wv.addJavascriptInterface(new JSMethod(wv.getContext()), "runNative");
// 防止安卓手机放大webview中的字体
settings.setTextZoom(100);
settings.setUserAgentString("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36");
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
settings.setAppCacheEnabled(true);
settings.setDomStorageEnabled(true);
settings.setDatabaseEnabled(true);
settings.setSupportZoom(false);
settings.setBuiltInZoomControls(false);
// todo update 同步cookie
synCookies(wv.getContext());
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
WXLogUtils.v("tag", "onPageOverride " + url);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
WXLogUtils.v("tag", "onPageStarted test" + url);
if (mOnPageListener != null) {
mOnPageListener.onPageStart(url);
}
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
WXLogUtils.v("tag", "onPageFinished " + url);
if (mOnPageListener != null) {
mOnPageListener.onPageFinish(url, view.canGoBack(), view.canGoForward());
}
if (mOnMessageListener != null) {
evaluateJS("javascript:(window.postMessage = function(message, targetOrigin) {"
+ "if (message == null || !targetOrigin) return;"
+ (DOWNGRADE_JS_INTERFACE
? "prompt(''" + BRIDGE_NAME + "://postMessage?message='' + JSON.stringify(message) + ''&targetOrigin='' + targetOrigin)"
: BRIDGE_NAME + ".postMessage(JSON.stringify(message), targetOrigin);")
+ "})");
}
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (mOnErrorListener != null) {
//mOnErrorListener.onError("error", "page error code:" + error.getErrorCode() + ", desc:" + error.getDescription() + ", url:" + request.getUrl());
mOnErrorListener.onError("error", "page error");
}
}
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
super.onReceivedHttpError(view, request, errorResponse);
if (mOnErrorListener != null) {
mOnErrorListener.onError("error", "http error");
}
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
if (mOnErrorListener != null) {
mOnErrorListener.onError("error", "ssl error");
}
}
});
wv.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
showWebView(newProgress == 100);
showProgressBar(newProgress != 100);
WXLogUtils.v("tag", "onPageProgressChanged " + newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (mOnPageListener != null) {
mOnPageListener.onReceivedTitle(view.getTitle());
}
}
@Override
public boolean onJsPrompt(WebView view, String url, String text, String defaultValue, JsPromptResult result) {
Uri uri = Uri.parse(text);
String scheme = uri.getScheme();
if (TextUtils.equals(scheme, BRIDGE_NAME)) {
if (TextUtils.equals(uri.getAuthority(), "postMessage")) {
String message = uri.getQueryParameter("message");
String targetOrigin = uri.getQueryParameter("targetOrigin");
onMessage(message, targetOrigin);
result.confirm("success");
} else {
result.confirm("fail");
}
return true;
}
return super.onJsPrompt(view, url, text, defaultValue, result);
}
});
if (!DOWNGRADE_JS_INTERFACE) {
wv.addJavascriptInterface(new Object() {
@JavascriptInterface
public void postMessage(String message, String targetOrigin) {
onMessage(message, targetOrigin);
}
}, BRIDGE_NAME);
}
}
private void onMessage(String message, String targetOrigin) {
if (message != null && targetOrigin != null && mOnMessageListener != null) {
try {
Map<String, Object> initData = new HashMap<>();
initData.put("data", JSON.parse(message));
initData.put("origin", targetOrigin);
initData.put("type", "message");
Message threadMessage = new Message();
threadMessage.what = POST_MESSAGE;
threadMessage.obj = initData;
mMessageHandler.sendMessage(threadMessage);
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}
private void evaluateJS(String jsStr) {
if (SDK_VERSION < 19) {
mWebView.loadUrl(jsStr);
} else {
mWebView.evaluateJavascript(jsStr, null);
}
}
private static class MessageHandler extends Handler {
private final WeakReference<WXWebView> mWv;
private MessageHandler(WXWebView wv) {
mWv = new WeakReference<>(wv);
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case POST_MESSAGE:
if (mWv.get() != null && mWv.get().mOnMessageListener != null) {
mWv.get().mOnMessageListener.onMessage((Map<String, Object>) msg.obj);
}
break;
default:
break;
}
}
}
public static class JSMethod {
private Context mContext;
public JSMethod(Context mContext) {
this.mContext = mContext;
}
@JavascriptInterface
public void closePage() {
//关闭当前页面
RouterTracker.popActivity();
}
public void resignKeyboard() {
YiduCommonUtil.resignKeyboard(mContext);
}
}
}
版本号控制:
../platforms/android/app/build.gradle
- VersionCode:对消费者不可见,仅用于应用市场、程序内部识别版本,判断新旧等用途。应用商店就会以这个[VersionCode]为准,来判断版本。安装包的[VersionCode]数字越大就越新。一般[VersionCode]都是一个正整数
- VersionName:展示给消费者,消费者会通过它认知自己安装的版本
defaultConfig {
applicationId ""
minSdkVersion project.appMinSdkVersion
targetSdkVersion project.targetSdkVersion
versionCode 2
versionName "1.0.8" // here
ndk {
abiFilters "x86"
abiFilters "armeabi"
}
}
外部应用调起安卓app :
参考:https://www.jianshu.com/p/8e13860cb6da
通过scheme跳转:.../platforms/android/sdk/src/main/AndroidManifest.xml中添加一个activity
<!-- schema -->
<activity
android:name="com.weex.app.SchemeActivity"
android:label="@string/app_name">
<!-- 要想在别的App上能成功调起App,必须添加intent过滤器 -->
<intent-filter>
<!--协议部分,随便设置-->
<!-- <data android:scheme="ydmisdiag" android:host="com.yiducloud.misdiag" android:path="/"/> -->
<data android:scheme="ydmisdiag"/>
<!--下面这几行也必须得设置-->
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>
新建../platforms/android/app/src/main/java/com/weex/app/SchemeActivity.java
package com.weex.app;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
public class SchemeActivity extends AbsWeexActivity {
private static final String TAG = "SchemeActivity";
private TextView schemeTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent();
intent.setClass(SchemeActivity.this, WXPageActivity.class);
//intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
}
实现按返回键三次后退出应用:
新建../platforms/android/app/src/main/java/com/weex/app/StorageActivity.java , 用来存储实例activity
package com.weex.app;
import android.app.Activity;
import android.app.Application;
import java.util.LinkedList;
import java.util.List;
public class StorageActivity extends Application {
private List<Activity> activityList = new LinkedList<Activity>();
private static StorageActivity instance;
private StorageActivity() {
}
@Override
public void onCreate() {
super.onCreate();
}
//单例模式中获取唯一的StorageActivity实例
public static StorageActivity getInstance() {
if(null == instance) {
instance = new StorageActivity();
}
return instance;
}
//添加Activity到容器中
public void addActivity(Activity activity) {
activityList.add(activity);
}
//遍历所有Activity并finish
public void exit() {
for(Activity activity:activityList) {
activity.finish();
}
activityList.clear();
}
}
在.../platforms/android/app/src/main/java/com/weex/app/AbsWeexActivity.java中新增


package com.weex.app;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.taobao.weex.IWXRenderListener;
import com.taobao.weex.WXSDKEngine;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.common.IWXDebugProxy;
import com.taobao.weex.common.WXRenderStrategy;
import com.weex.app.util.CommonUtils;
import java.util.HashMap;
import java.util.Map;
public abstract class AbsWeexActivity extends AppCompatActivity implements IWXRenderListener {
private static final String TAG = "AbsWeexActivity";
protected BroadcastReceiver mBroadcastReceiver;
protected ViewGroup mContainer;
protected WXSDKInstance mInstance;
protected Uri mUri;
private WxReloadListener mReloadListener;
private WxRefreshListener mRefreshListener;
private String mUrl;// "http://your_current_IP:12580/examples/build/index.js";
private String mPageName = TAG;
protected Boolean isLocalUrl = false;
private static boolean isExit = false; // 标识是否退出
private static int count = 0; // 标识按键次数
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
isExit = false;
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 添加Activity到堆栈
StorageActivity.getInstance().addActivity(this);
createWeexInstance();
mInstance.onActivityCreate();
registerBroadcastReceiver(mBroadcastReceiver, null);
}
protected final ViewGroup getContainer() {
return mContainer;
}
protected final void setContainer(ViewGroup container) {
mContainer = container;
}
protected void destoryWeexInstance() {
if (mInstance != null) {
mInstance.registerRenderListener(null);
mInstance.destroy();
mInstance = null;
}
}
protected void createWeexInstance() {
destoryWeexInstance();
mInstance = new WXSDKInstance(this);
mInstance.registerRenderListener(this);
}
protected void renderPageByURL(String url) {
renderPageByURL(url, null);
}
protected void renderPageByURL(String url, String jsonInitData) {
CommonUtils.throwIfNull(mContainer, new RuntimeException("Can''t render page, container is null"));
Map<String, Object> options = new HashMap<>();
options.put(WXSDKInstance.BUNDLE_URL, url);
mInstance.renderByUrl(
getPageName(),
url,
options,
jsonInitData,
CommonUtils.getDisplayWidth(this),
CommonUtils.getDisplayHeight(this),
WXRenderStrategy.APPEND_ASYNC);
}
public String getPageName() {
return mPageName;
}
@Override
public void onStart() {
super.onStart();
if (mInstance != null) {
mInstance.onActivityStart();
}
}
@Override
public void onResume() {
super.onResume();
if (mInstance != null) {
mInstance.onActivityResume();
}
}
@Override
public void onPause() {
super.onPause();
if (mInstance != null) {
mInstance.onActivityPause();
}
}
@Override
public void onStop() {
super.onStop();
if (mInstance != null) {
mInstance.onActivityStop();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (mInstance != null) {
mInstance.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mInstance != null) {
mInstance.onActivityResult(requestCode, resultCode, data);
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onDestroy() {
super.onDestroy();
if (mInstance != null) {
mInstance.onActivityDestroy();
}
unregisterBroadcastReceiver();
}
@Override
public void onViewCreated(WXSDKInstance wxsdkInstance, View view) {
if (mContainer != null) {
mContainer.removeAllViews();
mContainer.addView(view);
}
}
@Override
public void onRefreshSuccess(WXSDKInstance wxsdkInstance, int width, int height) {
}
public void runWithPermissionsCheck(int requestCode, String permission, Runnable runnable) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
Toast.makeText(this, "please give me the permission", Toast.LENGTH_SHORT).show();
} else {
ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode);
}
} else {
if (runnable != null) {
runnable.run();
}
}
}
@Override
public void onRenderSuccess(WXSDKInstance instance, int width, int height) {
}
@Override
public void onException(WXSDKInstance instance, String errCode, String msg) {
}
public void setReloadListener(WxReloadListener reloadListener) {
mReloadListener = reloadListener;
}
public void registerBroadcastReceiver(BroadcastReceiver receiver, IntentFilter filter) {
mBroadcastReceiver = receiver != null ? receiver : new DefaultBroadcastReceiver();
if (filter == null) {
filter = new IntentFilter();
}
filter.addAction(IWXDebugProxy.ACTION_DEBUG_INSTANCE_REFRESH);
filter.addAction(WXSDKEngine.JS_FRAMEWORK_RELOAD);
LocalBroadcastManager.getInstance(getApplicationContext())
.registerReceiver(mBroadcastReceiver, filter);
if (mReloadListener == null) {
setReloadListener(new WxReloadListener() {
@Override
public void onReload() {
createWeexInstance();
renderPage();
}
});
}
if (mRefreshListener == null) {
setRefreshListener(new WxRefreshListener() {
@Override
public void onRefresh() {
createWeexInstance();
renderPage();
}
});
}
}
public void unregisterBroadcastReceiver() {
if (mBroadcastReceiver != null) {
LocalBroadcastManager.getInstance(getApplicationContext())
.unregisterReceiver(mBroadcastReceiver);
mBroadcastReceiver = null;
}
setReloadListener(null);
setRefreshListener(null);
}
public void setRefreshListener(WxRefreshListener refreshListener) {
mRefreshListener = refreshListener;
}
public String getUrl() {
return mUrl;
}
public void setUrl(String url) {
mUrl = url;
}
public void loadUrl(String url) {
setUrl(url);
renderPage();
}
protected void preRenderPage() {
}
protected void postRenderPage() {
}
protected void renderPage() {
preRenderPage();
renderPageByURL(mUrl);
postRenderPage();
}
protected boolean isLocalPage() {
boolean isLocalPage = true;
if (mUri != null) {
String scheme = mUri.getScheme();
isLocalPage = !mUri.isHierarchical() ||
(!TextUtils.equals(scheme, "http") && !TextUtils.equals(scheme, "https"));
}
return isLocalPage;
}
public void setPageName(String pageName) {
mPageName = pageName;
}
public interface WxReloadListener {
void onReload();
}
public interface WxRefreshListener {
void onRefresh();
}
public class DefaultBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (IWXDebugProxy.ACTION_DEBUG_INSTANCE_REFRESH.equals(intent.getAction())) {
if (mRefreshListener != null) {
mRefreshListener.onRefresh();
}
} else if (WXSDKEngine.JS_FRAMEWORK_RELOAD.equals(intent.getAction())) {
if (mReloadListener != null) {
mReloadListener.onReload();
}
}
}
}
// 获取按键信息
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
exit();
return false;
}
return super.onKeyDown(keyCode, event);
}
// 按返回键三次后退出,2000秒内
private void exit() {
if (!isExit) {
count++;
if (count >= 2) {
isExit = true;
count = 0;
Toast.makeText(getApplicationContext(), "再按一次退出程序", Toast.LENGTH_SHORT).show();
mHandler.sendEmptyMessageDelayed(0, 2000);
}
} else {
StorageActivity.getInstance().exit();
System.exit(0);
/*finish();
System.exit(0);*/
}
}
}
空包签名(背景:app上架oppo应用市场,提示已有相同包名,需要认领)
mac下查看jdk路径 /usr/libexec/java_home -V
安装jdk,方便起见,将oppo提供的OppoSignVerify.apk同签名文件放在同一个文件夹
执行
jarsigner -verbose -keystore [签名文件] -signedjar OppoSignVerify_signed.apk OppoSignVerify.apk [签名文件]
Android beta APK分发问题
我明天在DC参加黑客新闻聚会,我将展示我的Android应用程序.我想有一个二维码,供人们扫描试用我的应用程序.
我是否应该关注将未发布的APK发给陌生人?我听说供应商将他们的测试版APK上传到其他人的帐户下.我计划最终以1美元的价格将它放在Android Market上.我上传了一个初始版本到Market来保存包名,但是什么阻止某人用他们的信息重新签名呢?这甚至可能吗?
我想用一个在一个月内到期的密钥签名,以防止人们永远运行测试版.这有用吗?
解决方法:
好吧,你似乎有几个选择.
1)将您的应用程序上传到市场并使用您的长期密钥进行签名.这样,在会议中安装应用程序的用户将能够接收更新.唯一的缺点是你必须将其作为付费应用程序发布.一旦它作为免费发布的应用程序,你无法改变它!
2)使用即将过期的密钥对应用程序进行签名.从Android Documentation开始.“如果您计划支持单个应用程序的升级,则应确保您的密钥的有效期超过该应用程序的预期使用期限.建议有效期为25年或更长时间.当您的密钥有效期为到期后,用户将无法再无缝升级到您的应用程序的新版本.“似乎用户仍然可以运行您的应用程序,但在密钥到期时不能升级.
3)让您的应用程序通过检查系统时间强制执行“测试版”,如果它超过某个点,则不允许访问您的应用.
android EditText的android:gravity="right"和android:hint="开源"两属性不能同时生效的有关问题
当我用gravity="right"时 hint提示信息就没法出现。不写java代码只是在xml布局文件中要怎样设置属性才能实现文本位置靠右而又有提示信息呢。android google map 开发问题
1 怎么根据用户点击地图位置的不同放大相应位置的地图?(比如点击福建就把福建的地图放大,再点击厦门就把厦门的地图放大)也就是说怎么知道用户点击了哪里?2 怎么根据用户手势的不同进行相应的操作?比如用手划向左边就把当前的视图经度变大,用手划向下边就把纬度减小?
3 overlay中的图片可以放大缩小吗?比如在指定的经纬度放了一张图片(overlay中)怎么根据地图放大缩小的倍数对overlay的图片也放大缩小?
Android Weex 填坑记
Weex 自带的 modal 里面有 toast 方法,在 Android 里面的实现文件是 WXModalUIModule.java,
private Toast toast;
@JSMethod(uiThread = true)
public void toast(String param) {
String message = "";
int duration = Toast.LENGTH_SHORT;
if (!TextUtils.isEmpty(param)) {
try {
param = URLDecoder.decode(param, "utf-8");
JSONObject jsObj = JSON.parseObject(param);
message = jsObj.getString(MESSAGE);
duration = jsObj.getInteger(DURATION);
} catch (Exception e) {
WXLogUtils.e("[WXModalUIModule] alert param parse error ", e);
}
}
if (TextUtils.isEmpty(message)) {
WXLogUtils.e("[WXModalUIModule] toast param parse is null ");
return;
}
if (duration > 3) {
duration = Toast.LENGTH_LONG;
} else {
duration = Toast.LENGTH_SHORT;
}
if (toast == null) {
toast = Toast.makeText(mWXSDKInstance.getContext(), message, duration);
} else {
toast.setDuration(duration);
toast.setText(message);
}
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
可以看出来是复用了一个 toast 来实现,但是段时间内重复弹却不会弹多个 toast 出来,原因是因为在 NotificationManagerService 里面做了处理,调用 toast.show () 最终会走到 NotificationManagerService.enqueueToast 方法,在这个方法里面,对于当前已经在队列的会更新 duration 时间。
synchronized (mToastQueue) {
int callingPid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
try {
ToastRecord record;
int index = indexOfToastLocked(pkg, callback);
// If it''s already in the queue, we update it in place, we don''t
// move it to the end of the queue.
if (index >= 0) {
record = mToastQueue.get(index);
record.update(duration);
} else {
// Limit the number of toasts that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemToast) {
int count = 0;
final int N = mToastQueue.size();
for (int i=0; i<N; i++) {
final ToastRecord r = mToastQueue.get(i);
if (r.pkg.equals(pkg)) {
count++;
if (count >= MAX_PACKAGE_NOTIFICATIONS) {
Slog.e(TAG, "Package has already posted " + count
+ " toasts. Not showing more. Package=" + pkg);
return;
}
今天关于weex-android 开发问题和android wear开发的讲解已经结束,谢谢您的阅读,如果想了解更多关于Android beta APK分发问题、android EditText的android:gravity="right"和android:hint="开源"两属性不能同时生效的有关问题、android google map 开发问题、Android Weex 填坑记的相关知识,请在本站搜索。
本文标签: