├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── values
│ │ │ │ ├── styles.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── strings.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_web_view.xml
│ │ │ │ ├── activity_momory.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── activity_welcome.xml
│ │ │ │ └── activity_auto_split.xml
│ │ │ ├── drawable
│ │ │ │ ├── progress_bar_list.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── chenjiajuan
│ │ │ │ └── service
│ │ │ │ ├── AutoSplitActivity.java
│ │ │ │ ├── QRTaskListener.java
│ │ │ │ ├── WelcomeActivity.java
│ │ │ │ ├── NetWorkUtils.java
│ │ │ │ ├── MemoryTestActivity.java
│ │ │ │ ├── LoginWebView.java
│ │ │ │ ├── AutoSplitTextView.java
│ │ │ │ ├── LoginServiceActivity.java
│ │ │ │ ├── LoginWebViewService.java
│ │ │ │ └── TestActivity.java
│ │ ├── aidl
│ │ │ └── com
│ │ │ │ └── chenjiajuan
│ │ │ │ └── service
│ │ │ │ ├── IWebViewService.aidl
│ │ │ │ └── IWebViewCallback.aidl
│ │ ├── assets
│ │ │ └── javascript.html
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── chenjiajuan
│ │ │ └── service
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── chenjiajuan
│ │ └── service
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
└── .gitignore
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenjiajuan/AidlServiceDemo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | gradle.properties
11 | gradle/
12 | gradlew
13 | gradlew.bat
14 | settings.gradle
15 | .idea/
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WebViewServiceDemo
3 | WebViewActivity
4 | WelcomeActivity
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/AutoSplitActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | public class AutoSplitActivity extends Activity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_auto_split);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_web_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/aidl/com/chenjiajuan/service/IWebViewService.aidl:
--------------------------------------------------------------------------------
1 | // IWebViewService.aidl
2 | package com.chenjiajuan.service;
3 | import com.chenjiajuan.service.IWebViewCallback;
4 |
5 | // Declare any non-default types here with import statements
6 |
7 | interface IWebViewService {
8 | /**
9 | * Demonstrates some basic types that you can use as parameters
10 | * and return values in AIDL.
11 | */
12 | void doLoadWebViewJsUrl(in IWebViewCallback webViewCallback);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_momory.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/test/java/com/chenjiajuan/service/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/aidl/com/chenjiajuan/service/IWebViewCallback.aidl:
--------------------------------------------------------------------------------
1 | // IWebViewCallback.aidl
2 | package com.chenjiajuan.service;
3 |
4 | // Declare any non-default types here with import statements
5 |
6 | interface IWebViewCallback {
7 | /**
8 | * Demonstrates some basic types that you can use as parameters
9 | * and return values in AIDL.
10 | */
11 | void showQRCode(in String url);
12 |
13 | void onQRLoginSuccess(in String userInfo);
14 |
15 | void onQRLoginFailure(in int code, in String msg);
16 |
17 |
18 | void onQRScanCodeSuccess(in int code, in String msg);
19 |
20 |
21 | void onQRRefresh(in int code, in String msg);
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/QRTaskListener.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 |
4 | /**
5 | * Created by chenjiajuan on 2018/6/7.
6 | */
7 |
8 | public interface QRTaskListener {
9 |
10 | /**
11 | * 手机端确认成功
12 | *
13 | * @param userInfo
14 | */
15 | void onQRLoginSuccess(String userInfo);
16 |
17 | /**
18 | * 登录失败
19 | *
20 | * @param code
21 | * @param msg
22 | */
23 |
24 | void onQRLoginFailure(int code, String msg);
25 |
26 | /**
27 | * 扫码成功
28 | *
29 | * @param code
30 | * @param msg
31 | */
32 |
33 | void onQRScanCodeSuccess(int code, String msg);
34 |
35 | /**
36 | * 刷新二维码
37 | *
38 | * @param code
39 | * @param msg
40 | */
41 |
42 | void onQRRefresh(int code, String msg);
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/WelcomeActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 |
8 | public class WelcomeActivity extends Activity {
9 | private Intent intent;
10 |
11 | @Override
12 | protected void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | setContentView(R.layout.activity_welcome);
15 | intent=new Intent();
16 | }
17 |
18 | public void onNormal(View view){
19 | intent.setClass(this,MemoryTestActivity.class);
20 | startActivity(intent);
21 |
22 | }
23 |
24 | public void onProcess(View view){
25 | intent.setClass(this,LoginServiceActivity.class);
26 | startActivity(intent);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/chenjiajuan/service/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.chenjiajuan.webviewservicedemo", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_welcome.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
15 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 | defaultConfig {
6 | applicationId "com.chenjiajuan.webviewservicedemo"
7 | minSdkVersion 15
8 | targetSdkVersion 26
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 |
20 | android.applicationVariants.all { variant ->
21 | variant.outputs.all {output ->
22 | outputFileName=output.outputFile.getParent()+"test" + defaultConfig.versionName + "-Release.apk"
23 |
24 | }
25 | }
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 | implementation 'com.android.support.constraint:constraint-layout:1.1.2'
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/assets/javascript.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Android 、JS
6 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/progress_bar_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
11 |
12 |
13 |
14 | -
15 |
16 |
17 |
18 |
23 |
24 |
25 |
26 |
27 | -
28 |
29 |
30 |
31 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/NetWorkUtils.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.content.Context;
4 | import android.net.ConnectivityManager;
5 | import android.net.NetworkInfo;
6 |
7 | /**
8 | *Created by chenjiajuan on 2018/6/7.
9 | */
10 |
11 | public class NetWorkUtils {
12 | /**
13 | * 判断是否有网络连接
14 | * @param context
15 | * @return
16 | */
17 | public static boolean isNetworkConnected(Context context) {
18 | if (context != null) {
19 | ConnectivityManager mConnectivityManager = (ConnectivityManager) context
20 | .getSystemService(Context.CONNECTIVITY_SERVICE);
21 | NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
22 | if (mNetworkInfo != null) {
23 | return mNetworkInfo.isAvailable();
24 | }
25 | }
26 | return false;
27 | }
28 |
29 | public static boolean isWifiConnected(Context context) {
30 | if (context != null) {
31 | ConnectivityManager mConnectivityManager = (ConnectivityManager) context
32 | .getSystemService(Context.CONNECTIVITY_SERVICE);
33 | NetworkInfo mWiFiNetworkInfo = mConnectivityManager
34 | .getNetworkInfo(ConnectivityManager.TYPE_WIFI);
35 | if (mWiFiNetworkInfo != null) {
36 | return mWiFiNetworkInfo.isAvailable();
37 | }
38 | }
39 | return false;
40 | }
41 |
42 | public static boolean canConnectNetWork(Context context){
43 | if (isNetworkConnected(context)||isWifiConnected(context))
44 | return true;
45 | return false;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/MemoryTestActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.view.ViewGroup;
6 | import android.view.ViewParent;
7 | import android.webkit.WebChromeClient;
8 | import android.webkit.WebView;
9 |
10 | /**
11 | * Created by chenjiajuan on 2018/6/7.
12 | * 常规webview内存泄漏的方案
13 | */
14 | public class MemoryTestActivity extends Activity {
15 | private WebView webView;
16 | private String url="https://www.jianshu.com/u/772becec8ed4";
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.activity_momory);
22 | webView = new WebView(this);
23 | webView.setWebChromeClient(new WebChromeClient());
24 | webView.getSettings().setBlockNetworkImage(true);
25 | webView.getSettings().setJavaScriptEnabled(true);
26 | webView.getSettings().setLoadsImagesAutomatically(false);
27 | webView.loadUrl(url);
28 | ViewGroup.LayoutParams params=new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
29 | addContentView(webView, params);
30 | }
31 |
32 | @Override
33 | protected void onDestroy() {
34 | ViewParent parent = webView.getParent();
35 | if (parent != null) {
36 | ((ViewGroup) parent).removeView(webView);
37 | }
38 | webView.stopLoading();
39 | webView.getSettings().setJavaScriptEnabled(false);
40 | webView.clearHistory();
41 | webView.clearView();
42 | webView.removeAllViews();
43 | webView.destroy();
44 | webView=null;
45 | super.onDestroy();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/LoginWebView.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.content.Context;
4 | import android.webkit.WebChromeClient;
5 | import android.webkit.WebResourceResponse;
6 | import android.webkit.WebView;
7 | import android.webkit.WebViewClient;
8 | import android.widget.LinearLayout;
9 |
10 | /**
11 | * Created by chenjiajuan on 2018/6/7.
12 | */
13 |
14 | public class LoginWebView extends LinearLayout {
15 | private static final String TAG = "LoginWebView";
16 | private WebView webView;
17 | private QRCodeListener qrCodeListener;
18 | private String url = "xxxxxxxx"; //测试url请填自己的
19 |
20 | public interface QRCodeListener {
21 | void fetchLoginUrl(String url);
22 |
23 | }
24 |
25 | public QRTaskListener qrTaskListener;
26 |
27 | public LoginWebView(Context context) {
28 | super(context);
29 | initView(context);
30 | }
31 |
32 | private void initView(Context context) {
33 | webView = new WebView(context);
34 | webView.setWebChromeClient(new WebChromeClient());
35 | webView.setWebViewClient(new LoginInterceptWebView());
36 | webView.getSettings().setBlockNetworkImage(true);
37 | webView.getSettings().setJavaScriptEnabled(true);
38 | webView.getSettings().setLoadsImagesAutomatically(false);
39 |
40 | }
41 | public class LoginInterceptWebView extends WebViewClient {
42 | public LoginInterceptWebView() {
43 | }
44 |
45 | @Override
46 | public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
47 | qrCodeListener.fetchLoginUrl(url);
48 | return super.shouldInterceptRequest(view, url);
49 | }
50 | }
51 |
52 | public void showQRCode(QRCodeListener listener) {
53 | this.qrCodeListener = listener;
54 | webView.loadUrl(url);
55 |
56 |
57 | }
58 |
59 | public void setQrTaskListener(QRTaskListener qrTaskListener) {
60 | this.qrTaskListener = qrTaskListener;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_auto_split.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
28 |
29 |
38 |
39 |
51 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/AutoSplitTextView.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.util.AttributeSet;
7 | import android.widget.TextView;
8 |
9 | public class AutoSplitTextView extends TextView{
10 | private String autoText;
11 | private float textWidth;
12 | private float textHeight;
13 | private Paint textPaint;
14 |
15 | public AutoSplitTextView(Context context) {
16 | this(context,null);
17 | }
18 |
19 | public AutoSplitTextView(Context context, AttributeSet attrs) {
20 | this(context, attrs,0);
21 | }
22 |
23 | public AutoSplitTextView(Context context, AttributeSet attrs, int defStyleAttr) {
24 | super(context, attrs, defStyleAttr);
25 | }
26 |
27 | //解决首次渲染,没有补全的bug。重新x
28 | int mWidth = -1;
29 |
30 | @Override
31 | protected void onDraw(Canvas canvas) {
32 | if(mWidth != getWidth() || !autoText.equals(getText().toString()) ){
33 | autoText=autoSplitText(this);
34 | setText(autoText);
35 | mWidth = getWidth();
36 | }
37 | super.onDraw(canvas);
38 |
39 | }
40 |
41 | private String autoSplitText(AutoSplitTextView textView) {
42 | CharSequence rawCharSequence = textView.getText();
43 | String originText = rawCharSequence.toString();//获取原始文本
44 | textPaint = textView.getPaint();// 获取画笔
45 | textWidth = textView.getWidth() - textView.getPaddingLeft() - textView.getPaddingRight();
46 | textHeight = textView.getHeight();
47 | String allTextLines=originText.replaceAll("\n","");
48 | StringBuilder stringBuilder = new StringBuilder();
49 | if (textPaint.measureText(allTextLines)>textWidth){
50 | //如果整行宽度超过控件所用宽度,则按字符测量,在超过可用宽度的最后一个字符添加换行符
51 | float lineWidth = 0;
52 | for (int i = 0; i < allTextLines.length(); i++) {
53 | char textChar = allTextLines.charAt(i);
54 | lineWidth += textPaint.measureText(String.valueOf(textChar));
55 | if (lineWidth <= textWidth) {
56 | stringBuilder.append(textChar);
57 | } else {
58 | stringBuilder.append("\n");
59 | i--;
60 | lineWidth = 0;
61 | }
62 | }
63 | }else {
64 | stringBuilder.append(allTextLines);
65 | }
66 |
67 | return stringBuilder.toString();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/LoginServiceActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.app.Activity;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.ServiceConnection;
8 | import android.os.Bundle;
9 | import android.os.IBinder;
10 | import android.os.RemoteException;
11 | import android.util.Log;
12 | import android.widget.TextView;
13 |
14 | /**
15 | * service进程加载webview
16 | * aidl通信
17 | * Created by chenjiajuan on 2018/6/7.
18 | */
19 |
20 | public class LoginServiceActivity extends Activity {
21 | private static final String TAG = "LoginServiceActivity";
22 | private TextView tvQrCode;
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_main);
28 | tvQrCode=findViewById(R.id.tvQrCode);
29 | Intent intent = new Intent();
30 | intent.setPackage(this.getPackageName());
31 | intent.setAction("com.chenjiajuan.webview");
32 | bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
33 |
34 | }
35 |
36 | /**
37 | * 设置连接,获取service,绑定callback
38 | */
39 | private ServiceConnection serviceConnection = new ServiceConnection() {
40 | @Override
41 | public void onServiceConnected(ComponentName name, IBinder service) {
42 | IWebViewService webViewService = IWebViewService.Stub.asInterface(service);
43 | if (webViewService == null)
44 | return;
45 | try {
46 | Log.e(TAG, "onServiceConnected......");
47 | webViewService.doLoadWebViewJsUrl(webViewCallback);
48 | } catch (RemoteException e) {
49 | e.printStackTrace();
50 | }
51 | }
52 |
53 | @Override
54 | public void onServiceDisconnected(ComponentName name) {
55 |
56 | }
57 | };
58 | /**
59 | * 设置callback,处理数据
60 | * 简单举例showQRCode得到servcie进程回调过来的信息
61 | */
62 | private IWebViewCallback webViewCallback = new IWebViewCallback.Stub() {
63 |
64 | /**
65 | * 获取到url,decode成二维码图片
66 | * @param url
67 | * @throws RemoteException
68 | */
69 | @Override
70 | public void showQRCode(final String url) throws RemoteException {
71 | tvQrCode.post(new Runnable() {
72 | @Override
73 | public void run() {
74 | tvQrCode.setText("获取到的url : "+url);
75 | }
76 | });
77 |
78 | Log.d(TAG,"url : "+url);
79 |
80 | }
81 |
82 | /**
83 | * 扫码登录成功
84 | * @param userInfo
85 | * @throws RemoteException
86 | */
87 |
88 | @Override
89 | public void onQRLoginSuccess(String userInfo) throws RemoteException {
90 |
91 | }
92 |
93 | /**
94 | * 扫码登录失败
95 | * @param code
96 | * @param msg
97 | * @throws RemoteException
98 | */
99 |
100 | @Override
101 | public void onQRLoginFailure(int code, String msg) throws RemoteException {
102 |
103 | }
104 |
105 | /**
106 | * 扫码成功
107 | * @param code
108 | * @param msg
109 | * @throws RemoteException
110 | */
111 |
112 | @Override
113 | public void onQRScanCodeSuccess(int code, String msg) throws RemoteException {
114 |
115 | }
116 |
117 | /**
118 | * 刷新二维码
119 | * @param code
120 | * @param msg
121 | * @throws RemoteException
122 | */
123 | @Override
124 | public void onQRRefresh(int code, String msg) throws RemoteException {
125 |
126 |
127 | }
128 | };
129 |
130 | @Override
131 | protected void onDestroy() {
132 | super.onDestroy();
133 | unbindService(serviceConnection);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/LoginWebViewService.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.Handler;
6 | import android.os.IBinder;
7 | import android.os.Message;
8 | import android.os.RemoteException;
9 |
10 | import java.lang.ref.WeakReference;
11 |
12 | /**
13 | * Created by chenjiajuan on 2018/6/7.
14 | */
15 |
16 | public class LoginWebViewService extends Service {
17 | private static final String TAG = "LoginWebViewService";
18 | private LoginWebView loginWebView;
19 | private IWebViewCallback callback;
20 | private WebViewHandler webViewHandler = new WebViewHandler(this);
21 |
22 | /**
23 | * 由于加载webview页面必须在主线程,所以此处采用了handler
24 | */
25 | private IWebViewService webViewService = new IWebViewService.Stub() {
26 | @Override
27 | public void doLoadWebViewJsUrl(final IWebViewCallback webViewCallback) throws RemoteException {
28 | Message message=new Message();
29 | message.what=0;
30 | message.obj=webViewCallback;
31 | webViewHandler.sendMessage(message);
32 |
33 | }
34 | };
35 |
36 | private static class WebViewHandler extends Handler {
37 | private WeakReference weakReference;
38 | public WebViewHandler(LoginWebViewService webViewService) {
39 | this.weakReference = new WeakReference<>(webViewService);
40 | }
41 | @Override
42 | public void handleMessage(Message msg) {
43 | switch (msg.what){
44 | case 0:
45 | weakReference.get().callback = (IWebViewCallback) msg.obj;
46 | weakReference.get().loginWebView.showQRCode(new LoginWebView.QRCodeListener() {
47 | @Override
48 | public void fetchLoginUrl(String url) {
49 | try {
50 | weakReference.get().callback.showQRCode(url);
51 | } catch (RemoteException e) {
52 | e.printStackTrace();
53 | }
54 | }
55 | });
56 | break;
57 | }
58 | }
59 | }
60 |
61 |
62 | /**
63 | * 使用Webview的回调,通过Activity内的callback,返回状态给Activity
64 | * webveiw--->Service-->Activity
65 | */
66 | private class LoginQRTask implements QRTaskListener {
67 | public LoginQRTask() {
68 |
69 | }
70 |
71 | @Override
72 | public void onQRLoginSuccess(String userInfo) {
73 | try {
74 | callback.onQRLoginSuccess(userInfo);
75 | } catch (RemoteException e) {
76 | e.printStackTrace();
77 | }
78 | }
79 |
80 | @Override
81 | public void onQRLoginFailure(int code, String msg) {
82 | try {
83 | callback.onQRLoginFailure(code, msg);
84 | } catch (RemoteException e) {
85 | e.printStackTrace();
86 | }
87 |
88 | }
89 |
90 | @Override
91 | public void onQRScanCodeSuccess(int code, String msg) {
92 | try {
93 | callback.onQRScanCodeSuccess(code, msg);
94 | } catch (RemoteException e) {
95 | e.printStackTrace();
96 | }
97 | }
98 |
99 | @Override
100 | public void onQRRefresh(int code, String msg) {
101 | try {
102 | callback.onQRRefresh(code, msg);
103 | } catch (RemoteException e) {
104 | e.printStackTrace();
105 | }
106 | }
107 | }
108 |
109 |
110 | @Override
111 | public void onCreate() {
112 | super.onCreate();
113 | //初始化webview
114 | loginWebView = new LoginWebView(this);
115 | //设置监听
116 | loginWebView.setQrTaskListener(new LoginQRTask());
117 | }
118 |
119 | @Override
120 | public IBinder onBind(Intent intent) {
121 | return webViewService.asBinder();
122 | }
123 |
124 | @Override
125 | public boolean onUnbind(Intent intent) {
126 | super.onUnbind(intent);
127 | //干掉进程!!!!
128 | System.exit(0);
129 | return true;
130 | }
131 |
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/java/com/chenjiajuan/service/TestActivity.java:
--------------------------------------------------------------------------------
1 | package com.chenjiajuan.service;
2 |
3 | import android.graphics.Bitmap;
4 | import android.net.Uri;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.app.Activity;
8 | import android.util.Log;
9 | import android.view.KeyEvent;
10 | import android.view.ViewGroup;
11 | import android.webkit.JavascriptInterface;
12 | import android.webkit.JsPromptResult;
13 | import android.webkit.JsResult;
14 | import android.webkit.ValueCallback;
15 | import android.webkit.WebChromeClient;
16 | import android.webkit.WebSettings;
17 | import android.webkit.WebView;
18 | import android.webkit.WebViewClient;
19 | import android.widget.ProgressBar;
20 | import android.widget.RelativeLayout;
21 | import android.widget.Toast;
22 |
23 | import java.util.HashMap;
24 | import java.util.Set;
25 |
26 | /**
27 | * Created by chenjiajuan on 2018/6/7.
28 | * webView的基本属性
29 | * webView与Js交互的几种方式总结
30 | */
31 |
32 | public class TestActivity extends Activity {
33 | private static final String TAG=TestActivity.class.getSimpleName();
34 | // private String url="https://www.baidu.com";
35 | // private String url="https://blog.csdn.net/carson_ho/article/details/52693322";
36 | private String url="file:///android_asset/javascript.html";
37 | private RelativeLayout rootContent;
38 | private WebView webView;
39 | private WebSettings webSettings;
40 | private ProgressBar progressBar;
41 |
42 | @Override
43 | protected void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | setContentView(R.layout.activity_web_view);
46 | rootContent=findViewById(R.id.rootContent);
47 | webView=new WebView(this);
48 | ViewGroup.LayoutParams layoutParams=new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
49 | ViewGroup.LayoutParams.MATCH_PARENT);
50 | progressBar=new ProgressBar(this);
51 | RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(100,100);
52 | params.addRule(RelativeLayout.CENTER_VERTICAL);
53 | params.addRule(RelativeLayout.CENTER_HORIZONTAL);
54 | progressBar.setLayoutParams(params);
55 | progressBar.setMax(100);
56 | progressBar.setIndeterminateDrawable(getResources().getDrawable(R.drawable.progress_bar_list));
57 | rootContent.addView(webView,layoutParams);
58 | webSettings=webView.getSettings();
59 | setBasicProperties();
60 | setWebViewClient();
61 | webView.loadUrl(url);
62 | }
63 |
64 | public void setBasicProperties(){
65 | webSettings.setJavaScriptEnabled(true); //设置允许js交互
66 | webSettings.setJavaScriptCanOpenWindowsAutomatically(true);//支持通过JS打开新窗口
67 | //======
68 | webSettings.setLoadWithOverviewMode(true);//缩放至屏幕大小
69 | webSettings.setUseWideViewPort(true); //图片尺寸缩放值适合webView大小
70 | webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
71 | webSettings.setBuiltInZoomControls(true);//设置内置的缩放控件。若为false,则该WebView不可缩放
72 | webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件
73 | //======
74 | if (NetWorkUtils.canConnectNetWork(this)){
75 | webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //优先从网络获取数据
76 | }else {
77 | webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //优先使用本地缓存
78 | }
79 | webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能
80 | webSettings.setDatabaseEnabled(true); //开启 database storage API 功能
81 | webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能
82 | String cacheDirPath = getFilesDir().getAbsolutePath() + "webview";
83 | Log.e(TAG,"cacheDirPath : "+cacheDirPath);
84 | webSettings.setAppCachePath(cacheDirPath); //设置 Application Caches 缓存目录
85 | //====
86 | webSettings.setAllowFileAccess(true);//设置可以访问文件
87 | webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
88 | webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
89 | }
90 | private void setWebViewClient() {
91 | webView.setWebChromeClient(new WebChromeClient(){
92 | @Override
93 | public void onProgressChanged(WebView view, int newProgress) {
94 | Log.e(TAG,"onProgressChanged newProgress : "+newProgress);
95 | progressBar.setProgress(newProgress);
96 | super.onProgressChanged(view, newProgress);
97 | }
98 | @Override
99 | public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
100 | Log.e(TAG,"onJsAlert : url : "+url+", message : "+message+" ,result : "+result.toString());
101 | return true;
102 | }
103 |
104 | @Override
105 | public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
106 | Log.e(TAG,"onJsPrompt : url : "+url+", message : "+message+", defaultValue : "+defaultValue);
107 | result.confirm("true");
108 | return true;
109 | }
110 |
111 | @Override
112 | public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
113 | Log.e(TAG,"onJsConfirm : url : "+url+" ,message : "+message+" , result : "+result.toString());
114 | HashMap stringHashMap=parse(message);
115 | result.confirm();
116 | return true;
117 | }
118 | });
119 | webView.setWebViewClient(new WebViewClient(){
120 | @Override
121 | public void onPageStarted(WebView view, String url, Bitmap favicon) {
122 | if (progressBar.getParent()!=null){
123 | ( (ViewGroup) progressBar.getParent()).removeView(progressBar);
124 | }
125 | rootContent.addView(progressBar);
126 | super.onPageStarted(view, url, favicon);
127 | }
128 |
129 | @Override
130 | public void onPageFinished(WebView view, String url) {
131 | rootContent.removeView(progressBar);
132 | super.onPageFinished(view, url);
133 | }
134 | //在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次
135 | @Override
136 | public void onLoadResource(WebView view, String url) {
137 | super.onLoadResource(view, url);
138 | }
139 | //加载失败时,在此处给出友好的提示
140 | @Override
141 | public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
142 | switch (errorCode){
143 | case WebViewClient.ERROR_CONNECT:
144 | Toast.makeText(TestActivity.this,"请检查您的网络状态!",Toast.LENGTH_LONG).show();
145 | break;
146 | }
147 | super.onReceivedError(view, errorCode, description, failingUrl);
148 | }
149 |
150 | @Override
151 | public boolean shouldOverrideUrlLoading(WebView view, String url) {
152 | // parse(url);
153 | view.loadUrl(url);
154 | return true;
155 | }
156 |
157 | });
158 |
159 | // bindJsInterface();
160 | }
161 |
162 | @Override
163 | public boolean onKeyDown(int keyCode, KeyEvent event) {
164 | if (keyCode==KeyEvent.KEYCODE_BACK &&webView.canGoBack()){
165 | webView.goBack();
166 | return true;
167 | }
168 | return super.onKeyDown(keyCode, event);
169 | }
170 |
171 | @Override
172 | public boolean onKeyUp(int keyCode, KeyEvent event) {
173 | return super.onKeyUp(keyCode, event);
174 | }
175 |
176 | //--------------------- Js To Android ------------------//
177 |
178 | /**
179 | * JS调用Android的方案一
180 | */
181 | public void bindJsInterface(){
182 | webView.addJavascriptInterface(new JsToAndroid(),"JsToAndroid");
183 | }
184 |
185 | public class JsToAndroid extends Object{
186 | @JavascriptInterface
187 | public boolean alertWindow(String msg){
188 | Toast.makeText(TestActivity.this," JsToAndroid msg : "+msg,Toast.LENGTH_LONG).show();
189 | return true;
190 | }
191 | }
192 |
193 | /**
194 | * JS调用Android的方式二
195 | * 通过uri处理业务,那么如何返回值呢?====返回参数比较麻烦
196 | * webview.loadUrl(function()) 通过主动调用js的函数,将结果最为参数返回
197 | * @param url
198 | */
199 |
200 | public void parseUrl(String url){
201 | parse(url);
202 | }
203 |
204 | public HashMap parse(String url) {
205 | //拦截uri实行js调用native js://webview?username=111&password=222";
206 | HashMap hashMap = new HashMap<>();
207 | Uri uri = Uri.parse(url);
208 | if (uri.getScheme().equals("js")) {
209 | if (uri.getAuthority().equals("webview")) {
210 | Set stringSet = uri.getQueryParameterNames();
211 | for (String string : stringSet) {
212 | hashMap.put(string, uri.getQueryParameter(string));
213 | }
214 | }
215 | }
216 | return hashMap;
217 | }
218 |
219 | // ------------------- Android To Js --------------------//
220 | /**
221 | * 1.版本要求低
222 | * 2.需要刷新页面---效率低,交互不友好
223 | * 3.无法直接获取返回参数
224 | * 4.方便
225 | * @param view
226 | */
227 | public void AndroidToJs(WebView view){
228 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
229 | evaluateJavascript();
230 | }else {
231 | view.loadUrl("javascript:callJS()");
232 | }
233 | }
234 |
235 | /**
236 | * 1.版本要求高
237 | * 2.不需要刷新页面---效率高
238 | * 3.可以获取Js调用后的返参
239 | */
240 |
241 | private void evaluateJavascript() {
242 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
243 | Log.e(TAG,"evaluateJavascript");
244 | webView.evaluateJavascript("javascript:callJS()", new ValueCallback() {
245 | @Override
246 | public void onReceiveValue(String value) {
247 | //获取返回值
248 | Log.e(TAG,"onReceiveValue value : "+value);
249 | }
250 | });
251 | }
252 | }
253 |
254 |
255 |
256 | @Override
257 | protected void onResume() {
258 | webView.onResume();
259 | super.onResume();
260 | }
261 |
262 | @Override
263 | protected void onPause() {
264 | webView.onPause();
265 | super.onPause();
266 | }
267 |
268 | @Override
269 | protected void onDestroy() {
270 | if (webView!=null){
271 | webView.loadDataWithBaseURL(null,"", "text/html", "utf-8", null);
272 | webView.clearHistory();
273 | webView.clearCache(true);
274 | ((ViewGroup) webView.getParent()).removeView(webView);
275 | webView.destroy();
276 | webView=null;
277 | }
278 | super.onDestroy();
279 | }
280 | }
281 |
--------------------------------------------------------------------------------