GVKun编程网logo

android http 缓存(android缓存数据)

2

在本文中,我们将详细介绍androidhttp缓存的各个方面,并为您提供关于android缓存数据的相关解答,同时,我们也将为您带来关于androidimportandroid-async-http-

在本文中,我们将详细介绍android http 缓存的各个方面,并为您提供关于android缓存数据的相关解答,同时,我们也将为您带来关于android import android-async-http-master有错误、android jni 回调 转自 http://android.wooyd.org/JNIExa...、Android Studio:ping URL 时出错:http://google.com不允许到 google.com 的明文 HTTP 流量、Android View 的绘制流程之 Layout 和 Draw 过程详解 (二) Android View 绘制流程之 DecorView 与 ViewRootImplAndroid View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)Android View 的事件分发原理解析Android 自定义 View 详解Android View 的绘制流程之 Measure 过程详解 (一)的有用知识。

本文目录一览:

android http 缓存(android缓存数据)

android http 缓存(android缓存数据)

android http 缓存要如何写,存储文件?没有网络的时候从文件读出来?

android import android-async-http-master有错误

android import android-async-http-master有错误

我从github获得了一个名为android-async-http-master的android项目,这是下载链接:https://github.com/loopj/android-async-http
但是当我将项目导入eclipse时,它有一个错误.

我得到这样的错误:

import cz.msebera.android.httpclient.Header;
import cz.msebera.android.httpclient.HeaderElement;

所以我的问题是:项目包的名称是com.loopj.android.http,其中是cz.msebera.android.httpclient包?

解决方法:

你必须下载这个jar cz.msebera.android,然后导入你的项目

如果您使用android studio,只需在.gradle依赖项中添加此编译’cz.msebera.android:httpclient:4.3.6′

希望它有所帮助

android jni 回调 转自 http://android.wooyd.org/JNIExa...

android jni 回调 转自 http://android.wooyd.org/JNIExa...

Important notice <注意>

The instructions in these document are applicable to older Android firmwares. Starting with firmware version 1.5 the Android NDK has been released, which makes it much easier to deal with native code. If you are coding for firmware 1.5 or newer, it is strongly recommended to use Android NDK instead of following this guide.

JNI Examples for Android

Jurij Smakov
jurij@wooyd.org

Table of Contents

  • Licence
  • Introduction
  • Java interface
  • Native library implementation
    • Headers and global variables
    • Calling Java functions from Java and native threads
    • Implementation of other native functions
    • The JNI_OnLoad() function implementation
  • Building the native library
  • Using native functions in Java code
  • Unresolved issues and bugs

[*]Licence

This document and the code generated from it are subject to the following licence:
Copyright (C) 2009 Jurij Smakov <jurij@wooyd.org>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

[*]Introduction

While JNI is a pretty exciting way to greatly extend Android functionality and port existing software to it, to date there is not a lot of detailed documentation on how to create the native libraries and interface with them from the Android''s Java Virtual Machine (JVM). This document aims at filling this gap, by providing a comprehensive example of creating a native JNI library, and using it from Java.

This document has been generated from source usingnoweb, a literate programming tool. TheJNIExample.nw is the source in noweb format. It can be used to generate the document output in a variety of formats (for example, PDF), as well as generate the JNI example source code.

The complete Android project, including the source code generated fromJNIExample.nw is available fordownload. So, if you are impatient, just grab it and check out the "Building the native library" section [->], which describes prerequisites for the build and the build procedure itself.

This document is not a replacement for other general JNI documentation. If you are not familiar with JNI, you may want to have a look at the following resources:

  • Sun''s Java Native Interface guide
  • Java Native Interface: Programmer''s Guide and Specification
Also, there are a couple of blog entries, which contain some bits of useful information:
  • JNI in Android
  • How to add a new module to Android

If you notice any errors or omissions (there are a couple of known bugs and unresolved issues [->]), or have a suggestion on how to improve this document, feel free to contact me using the email address mentioned above.

[*]Java interface

We start by defining a Java class JNIExampleInterface, which will provide the interface to calling the native functions, defined in a native (C++) library. The native functions corresponding to Java functions will need to have matching call signatures (i.e. the count and types of the arguments, as well as return type). The easiest way to get the correct function signatures in the native library is to first write down their Java prototypes, and then use the javah tool to generate the native JNI header with native function prototypes. These can be cut and pasted into the C++ file for implementation.

The Java functions which are backed by the corresponding native functions are declared in a usual way, adding a native qualifier. We also want to demonstrate how we could do the callbacks, i.e. calling the Java code from native code. That leads to the following high-level view of our interface class:

<JNIExampleInterface.java>= package org.wooyd.android.JNIExample;

import android.os.Handler;
import android.os.Bundle;
import android.os.Message;
import org.wooyd.android.JNIExample.Data;

public class JNIExampleInterface {
    static Handler h;
    <Example constructors>     <Example native functions>     <Example callback> }

One valid question about this definition is why we need a Handler class attribute. It turns out that it will come in handy in situations, when the native library wants to pass some information to the Java process through a callback. If the callback will be called by a native thread (for extended discussion see "Calling Java functions" section [->]), and then will try to modify the application''s user interface (UI) in any way, an exception will be thrown, asAndroid only allows the thread which created the UI (the UI thread) to modify it. To overcome this problem we are going to use the message-passing interface provided by Handler to dispatch the data received by a callback to the UI thread, and allow it to do the UI modifications. In order for this to work, we are going to accept a Handler instance as an argument for non-trivial constructor (reasons for keeping trivial one will become apparent later), and save it in a class attribute, and that''s pretty much the only task for the constructor:

<Example constructors>= (<-U)     public JNIExampleInterface() {}
    public JNIExampleInterface(Handler h) {
        this.h = h;
    }

To illustrate various argument-passing techniques, we define three nativefunctions:

  • callVoid(): takes no arguments and returns nothing;
  • getNewData(): takes two arguments and constructs a new class instance using them;
  • getDataString(): extracts a value from an object, which is passed as an argument.

<Example native functions>= (<-U)     public static native void callVoid();
    public static native Data getNewData(int i, String s);
    public static native String getDataString(Data d);

The callback will receive a string as an argument, and dispatch it to the Handler instance recorded in the constructor, after wrapping it in a Bundle:

<Example callback>= (<-U)     public static void callBack(String s) {
        Bundle b = new Bundle();
        b.putString("callback_string", s);
        Message m = Message.obtain();
        m.setData(b);
        m.setTarget(h);
        m.sendToTarget();
    }

We also need a definition of a dummy Data class, used purely for illustrative purposes:

<Data.java>= package org.wooyd.android.JNIExample;

public class Data {
    public int i;
    public String s;
    public Data() {}
    public Data(int i, String s) {
        this.i = i;
        this.s = s;
    }
}

After the source files Data.java and JNIExampleInterface.java are compiled, we can generate the JNI header file, containing the prototypes of the native functions, corresponding to their Java counterparts:

$ javac -classpath /path/to/sdk/android.jar \         org/wooyd/android/JNIExample/*.java
$ javah -classpath . org.wooyd.android.JNIExample.JNIExampleInterface

Native library implementation

At a high level, the Java library (consisting, in this case, of a single source file JNIExample.cpp) will look like that:

<JNIExample.cpp>= <JNI includes> <Miscellaneous includes> <Global variables> #ifdef __cplusplus
extern "C" {
#endif<callVoid implementation> <getNewData implementation> <getDataString implementation> <initClassHelper implementation> <JNIOnLoad implementation> #ifdef __cplusplus
}
#endif

Headers and global variables

The following includes define the functions provided by Android''s version of JNI, as well as some useful helpers:

<JNI includes>= (<-U) #include <jni.h>
#include <JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>

Various other things which will come in handy:

<Miscellaneous includes>= (<-U) #include <string.h>
#include <unistd.h>
#include <pthread.h>

It is useful to have some global variables to cache things which we know will not change during the lifetime of our program, and can be safely used across multiple threads. One of such things is the JVM handle. We can retrieve it every time it''s needed (for example, using android::AndroidRuntime::getJavaVM() function), but as it does not change, it''s better to cache it.

We can also use global variables to cache the references to required classes. As described below, it is not always easy to do class resolution in native code, especially when it is done from native threads (see "Calling Java functions" section [->] for details). Here we are just providing the global variables to hold instances of Data and JNIExampleInterface class objects, as well as defining some constant strings which will come in handy:

<Global variables>= (<-U) static JavaVM *gJavaVM;
static jobject gInterfaceObject, gDataObject;
const char *kInterfacePath = "org/wooyd/android/JNIExample/JNIExampleInterface";
const char *kDataPath = "org/wooyd/android/JNIExample/Data";
Defines gJavaVM, jobject, kDataPath, kInterfacePath (links are to index).

[*]Calling Java functions from Java and native threads

The callVoid() function is the simplest one, as it does not take any arguments, and returns nothing. We will use it to illustrate how the data can be passed back to Java through the callback mechanism, by calling the Java callBack() function.

At this point it is important to recognize that there are two distinct possibilities here: the Java function may be called either from a thread which originated in Java or from a native thread, which has been started in the native code, and of which JVM has no knowledge of. In the former case the call may be performed directly, in the latter we must first attach the native thread to the JVM. That requires an additional layer, a native callback handler, which will do the right thing in either case. We will also need a function to create the native thread, so structurally the implementation will look like this:

<callVoid implementation>= (<-U) <Callback handler> <Thread start function> <callVoid function>

Native callback handler gets the JNI environment (attaching the native thread if necessary), uses a cached reference to the gInterfaceObject to get to JNIExampleInterface class, obtains callBack() method reference, and calls it:

<Callback handler>= (<-U) static void callback_handler(char *s) {
    int status;
    JNIEnv *env;
    bool isAttached = false;
   
    status = gJavaVM->GetEnv((void **) &env, JNI_VERSION_1_4);
    if(status < 0) {
        LOGE("callback_handler: failed to get JNI environment, "
             "assuming native thread");
        status = gJavaVM->AttachCurrentThread(&env, NULL);
        if(status < 0) {
            LOGE("callback_handler: failed to attach "
                 "current thread");
            return;
        }
        isAttached = true;
    }
    /* Construct a Java string */
    jstring js = env->NewStringUTF(s);
    jclass interfaceClass = env->GetObjectClass(gInterfaceObject);
    if(!interfaceClass) {
        LOGE("callback_handler: failed to get class reference");
        if(isAttached) gJavaVM->DetachCurrentThread();
        return;
    }
    /* Find the callBack method ID */
    jmethodID method = env->GetStaticMethodID(
        interfaceClass, "callBack", "(Ljava/lang/String;)V");
    if(!method) {
        LOGE("callback_handler: failed to get method ID");
        if(isAttached) gJavaVM->DetachCurrentThread();
        return;
    }
    env->CallStaticVoidMethod(interfaceClass, method, js);
    if(isAttached) gJavaVM->DetachCurrentThread();
}
Defines callback_handler (links are to index).

A few comments are in order:

  • The JNI environment, returned by the JNI GetEnv() function is unique for each thread, so must be retrieved every time we enter the function. TheJavaVM pointer, on the other hand, is per-program, so can be cached (you will see it done in the JNI_OnLoad() function), and safely used across threads.

  • When we attach a native thread, the associated Java environment comes with a bootstrap class loader. That means that even if we would try to get a class reference in the function (the normal way to do it would be to useFindClass() JNI function), it would trigger an exception. Because of that we use a cached copy of JNIExampleInterface object to get a class reference (amusingly, we cannot cache the reference to the class itself, as any attempt to use it triggers an exception from JVM, who thinks that such reference should not be visible to native code). This caching is also done in JNI_OnLoad(), which might be the only function called by Android Java implementation with a functional class loader.

  • In order to retrieve the method ID of the callBack() method, we need to specify its name and JNI signature. In this case the signature indicates that the function takes a java.lang.String object as an argument, and returns nothing (i.e. has return type void). Consult JNI documentation for more information on function signatures, one useful tip is that you can usejavap utility to look up the function signatures of non-native functions (for native functions the signature information is already included as comments into the header, generated by javah).

  • Someone more paranoid than me could use locking to avoid race conditions associated with setting and checking of the isAttached variable.

In order to test calling from native threads, we will also need a function which is started in a separate thread. Its only role is to call the callback handler:

<Thread start function>= (<-U) void *native_thread_start(void *arg) {
    sleep(1);
    callback_handler((char *) "Called from native thread");
}
Defines native_thread_start (links are to index).

We now have all necessary pieces to implement the native counterpart of the callVoid() function:

<callVoid function>= (<-U) /*
 * Class:     org_wooyd_android_JNIExample_JNIExampleInterface
 * Method:    callVoid
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_wooyd_android_JNIExample_JNIExampleInterface_callVoid
  (JNIEnv *env, jclass cls) {
    pthread_t native_thread;

    callback_handler((char *) "Called from Java thread");
    if(pthread_create(&native_thread, NULL, native_thread_start, NULL)) {
        LOGE("callVoid: failed to create a native thread");
    }
}
Defines JNICALL (links are to index).

Implementation of other native functions

The getNewData() function illustrates creation of a new Java object in the native library, which is then returned to the caller. Again, we use a cached Data object reference in order to obtain the class and create a new instance.
<getNewData implementation>= (<-U) /*
 * Class:     org_wooyd_android_JNIExample_JNIExampleInterface
 * Method:    getNewData
 * Signature: (ILjava/lang/String;)Lorg/wooyd/android/JNIExample/Data;
 */
JNIEXPORT jobject JNICALL Java_org_wooyd_android_JNIExample_JNIExampleInterface_getNewData
  (JNIEnv *env, jclass cls, jint i, jstring s) {
    jclass dataClass = env->GetObjectClass(gDataObject);
    if(!dataClass) {
        LOGE("getNewData: failed to get class reference");
        return NULL;
    }
    jmethodID dataConstructor = env->GetMethodID(
        dataClass, "<init>", "(ILjava/lang/String;)V");
    if(!dataConstructor) {
        LOGE("getNewData: failed to get method ID");
        return NULL;
    }
    jobject dataObject = env->NewObject(dataClass, dataConstructor, i, s);
    if(!dataObject) {
        LOGE("getNewData: failed to create an object");
        return NULL;
    }
    return dataObject;
}
Defines jobject (links are to index).

The getDataString() function illustrates how a value stored in an object''s attribute can be retrieved in a native function.

<getDataString implementation>= (<-U) /*
 * Class:     org_wooyd_android_JNIExample_JNIExampleInterface
 * Method:    getDataString
 * Signature: (Lorg/wooyd/android/JNIExample/Data;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_org_wooyd_android_JNIExample_JNIExampleInterface_getDataString
  (JNIEnv *env, jclass cls, jobject dataObject) {
    jclass dataClass = env->GetObjectClass(gDataObject);
    if(!dataClass) {
        LOGE("getDataString: failed to get class reference");
        return NULL;
    }
    jfieldID dataStringField = env->GetFieldID(
        dataClass, "s", "Ljava/lang/String;");
    if(!dataStringField) {
        LOGE("getDataString: failed to get field ID");
        return NULL;
    }
    jstring dataStringValue = (jstring) env->GetObjectField(
        dataObject, dataStringField);
    return dataStringValue;
}
Defines jstring (links are to index).

The JNI_OnLoad() function implementation

The JNI_OnLoad() function must be provided by the native library in order for the JNI to work with Android JVM. It will be called immediately after the native library is loaded into the JVM. We already mentioned a couple of tasks which should be performed in this function: caching of the global JavaVM pointer and caching of the object instances to enable us to call into Java. In addition, any native methods which we want to call from Java must be registered, otherwise Android JVM will not be able to resolve them. The overall structure of the function thus can be written down as follows:

<JNIOnLoad implementation>= (<-U) jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv *env;
    gJavaVM = vm;
    LOGI("JNI_OnLoad called");
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("Failed to get the environment using GetEnv()");
        return -1;
    }
    <Class instance caching>     <Native function registration>     return JNI_VERSION_1_4;
}

We need some way to cache a reference to a class, because native threads do nothave access to a functional classloader. As explained above, we can''t cache the class references themselves, as it makes JVM unhappy. Instead we cache instances of these classes, so that we can later retrieve class references using GetObjectClass() JNI function. One thing to remember is that these objects must be protected from garbage-collecting using NewGlobalRef(), as that guarantees that they will remain available to different threads during JVM lifetime. Creating the instances and storing them in the global variables is the job for the initClassHelper() function:

<initClassHelper implementation>= (<-U) void initClassHelper(JNIEnv *env, const char *path, jobject *objptr) {
    jclass cls = env->FindClass(path);
    if(!cls) {
        LOGE("initClassHelper: failed to get %s class reference", path);
        return;
    }
    jmethodID constr = env->GetMethodID(cls, "<init>", "()V");
    if(!constr) {
        LOGE("initClassHelper: failed to get %s constructor", path);
        return;
    }
    jobject obj = env->NewObject(cls, constr);
    if(!obj) {
        LOGE("initClassHelper: failed to create a %s object", path);
        return;
    }
    (*objptr) = env->NewGlobalRef(obj);

}
Defines initClassHelper (links are to index).

With this function defined, class instance caching is trivial:

<Class instance caching>= (<-U)     initClassHelper(env, kInterfacePath, &gInterfaceObject);
    initClassHelper(env, kDataPath, &gDataObject);

In order to register the native functions, we create an array of JNINativeMethod structures, which contain function names, signatures (they can be simply copied from the comments, generated by javah), and pointers to the implementing functions. This array is then passed to Android''s registerNativeMethods() function:

<Native function registration>= (<-U)     JNINativeMethod methods[] = {
        {
            "callVoid",
            "()V",
            (void *) Java_org_wooyd_android_JNIExample_JNIExampleInterface_callVoid
        },
        {
            "getNewData",
            "(ILjava/lang/String;)Lorg/wooyd/android/JNIExample/Data;",
            (void *) Java_org_wooyd_android_JNIExample_JNIExampleInterface_getNewData
        },
        {
            "getDataString",
            "(Lorg/wooyd/android/JNIExample/Data;)Ljava/lang/String;",
            (void *) Java_org_wooyd_android_JNIExample_JNIExampleInterface_getDataString
        }
    };
    if(android::AndroidRuntime::registerNativeMethods(
        env, kInterfacePath, methods, NELEM(methods)) != JNI_OK) {
        LOGE("Failed to register native methods");
        return -1;
    }

[*]Building the native library

In order to build the native library, you need to include Android''s native headers and link against native libraries. The only way I know to get those is to check out and build the entire Android source code, and then build it. Procedure is described in detail at Android Get Source page. Make sure that you use the branch tag matching your SDK version, for example code in the release-1.0 branch matches Android 1.1 SDK.

For an example of CXXFLAGS and LDFLAGS you need to use to create a shared library with Android toolchain, check out theMakefile, included in theexample project tarball. They are derived from build/core/combo/linux-arm.mk in Android source.

You will probably want to build the entire example project, so you will need a copy of the SDK as well. This code has been tested to build with Android''s 1.1 SDK and run on the currently released version of the phone. Once you downloaded the SDK and the example tarball and unpacked them, you can build the project using the command

ANDROID_DIR=/path/to/android/source SDK_DIR=/path/to/sdk make

Using native functions in Java code

We will now create a simple activity, taking advantage of the JNI functions. One non-trivial task we will have to do in onCreate() method of the activity is to load the native JNI library, to make the functions defined there accessible to Java. Overall structure:

<JNIExample.java>= package org.wooyd.android.JNIExample;<Imports> public class JNIExample extends Activity
{
    TextView callVoidText, getNewDataText, getDataStringText;
    Button callVoidButton, getNewDataButton, getDataStringButton;
    Handler callbackHandler;
    JNIExampleInterface jniInterface;

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         <Load JNI library>          <callVoid demo>          <getNewData demo>          <getDataString demo>     }
}

Imports needed to draw the UI and display it to the user:

<Imports>= (<-U) [D->]     import android.app.Activity;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;

Imports needed to enable communication between the Java callback and the UI thread:

<Imports>+= (<-U) [<-D->]     import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;

Imports for manipulation with the native library:

<Imports>+= (<-U) [<-D->]     import java.util.zip.*;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.FileOutputStream;
    import java.io.File;

We will also need access to our JNI interface class and toy Data class:

<Imports>+= (<-U) [<-D->]     import org.wooyd.android.JNIExample.JNIExampleInterface;
    import org.wooyd.android.JNIExample.Data;

Logging utilities will also come in handy:

<Imports>+= (<-U) [<-D]     import android.util.Log;

At this time the only officialy supported way to create an Android application is by using the Java API. That means, that no facilities are provided to easily build and package shared libraries, and automatically load themon application startup. One possible way to include the library into the application package (file with extension .apk) is to place it into theassets subdirectory of the Android project, created with activitycreator. During the package build it will be automatically included into the APK package, however we still will have to load it by hand when our application starts up. Luckily, the location where APK is installed is known, and APK is simply a ZIP archive, so we can extract the library file from Java and copy it into the application directory, allowing us to load it:

<Load JNI library>= (<-U)     try {
        String cls = "org.wooyd.android.JNIExample";
        String lib = "libjniexample.so";
        String apkLocation = "/data/app/" + cls + ".apk";
        String libLocation = "/data/data/" + cls + "/" + lib;
        ZipFile zip = new ZipFile(apkLocation);
        ZipEntry zipen = zip.getEntry("assets/" + lib);
        InputStream is = zip.getInputStream(zipen);
        OutputStream os = new FileOutputStream(libLocation);
        byte[] buf = new byte[8092];
        int n;
        while ((n = is.read(buf)) > 0) os.write(buf, 0, n);
        os.close();
        is.close();
        System.load(libLocation);
    } catch (Exception ex) {
         Log.e("JNIExample", "failed to install native library: " + ex);
    }

The rest simply demonstrates the functionality, provided by the native library, by calling the native functions and displaying the results. For the callVoid() demo we need to initialize a handler first, and pass it to the JNI interface class, to enable us to receive callback messages:

<callVoid demo>= (<-U) [D->]     callVoidText = (TextView) findViewById(R.id.callVoid_text);
    callbackHandler = new Handler() {
        public void handleMessage(Message msg) {
            Bundle b = msg.getData();
            callVoidText.setText(b.getString("callback_string"));
        }
    };
    jniInterface = new JNIExampleInterface(callbackHandler);

We also set up a button which will call callVoid() from the native library when pressed:

<callVoid demo>+= (<-U) [<-D]     callVoidButton = (Button) findViewById(R.id.callVoid_button);
    callVoidButton.setOnClickListener(new Button.OnClickListener() {
        public void onClick(View v) {
            jniInterface.callVoid();
            
        } 
    });

For getNewData() we pass the parameters to the native function and expect to get the Data object back:

<getNewData demo>= (<-U)     getNewDataText = (TextView) findViewById(R.id.getNewData_text);  
    getNewDataButton = (Button) findViewById(R.id.getNewData_button);
    getNewDataButton.setOnClickListener(new Button.OnClickListener() {
        public void onClick(View v) {
            Data d = jniInterface.getNewData(42, "foo");
            getNewDataText.setText(
                "getNewData(42, \"foo\") == Data(" + d.i + ", \"" + d.s + "\")");
        }
    });

And pretty much the same for getDataString():

<getDataString demo>= (<-U)     getDataStringText = (TextView) findViewById(R.id.getDataString_text);  
    getDataStringButton = (Button) findViewById(R.id.getDataString_button);
    getDataStringButton.setOnClickListener(new Button.OnClickListener() {
        public void onClick(View v) {
            Data d = new Data(43, "bar");
            String s = jniInterface.getDataString(d);
            getDataStringText.setText(
                "getDataString(Data(43, \"bar\")) == \"" + s + "\"");
        }
    });

Try pushing the buttons and see whether it actually works!

[*]Unresolved issues and bugs

Even though the example is fully functional, there are a couple unresolved issues remaining, which I was not able to figure out so far. Problems appear when you start the activity, then press the Back button to hide it, and then start it again. In my experience, calls to native functions in such restarted activity will fail spectacularly. callVoid() simply crashes with a segmentation fault, while calls to getNewData() and getDataString() cause JVM to abort with an error, because it is no longer happy with the globally cached object reference. It appears that activity restart somehow invalidates our cached object references, even though they are protected with NewGlobalRef(), and the activity is running within the original JVM (activity restart does not mean that JVM itself is restarted). I don''t have a good explanation on why that happens, so if you have any ideas, please let me know.

  • <Callback handler>: U1, D2
  • <callVoid demo>: U1, D2, D3
  • <callVoid function>: U1, D2
  • <callVoid implementation>: U1, D2
  • <Class instance caching>: U1, D2
  • <Data.java>: D1
  • <Example callback>: U1, D2
  • <Example constructors>: U1, D2
  • <Example native functions>: U1, D2
  • <getDataString demo>: U1, D2
  • <getDataString implementation>: U1, D2
  • <getNewData demo>: U1, D2
  • <getNewData implementation>: U1, D2
  • <Global variables>: U1, D2
  • <Imports>: U1, D2, D3, D4, D5, D6
  • <initClassHelper implementation>: U1, D2
  • <JNI includes>: U1, D2
  • <JNIExample.cpp>: D1
  • <JNIExample.java>: D1
  • <JNIExampleInterface.java>: D1
  • <JNIOnLoad implementation>: U1, D2
  • <Load JNI library>: U1, D2
  • <Miscellaneous includes>: U1, D2
  • <Native function registration>: U1, D2
  • <Thread start function>: U1, D2
  • callback_handler: D1, U2, U3
  • gJavaVM: D1, U2, U3
  • initClassHelper: D1, U2
  • JNICALL: D1, U2, U3
  • jobject: D1, D2, U3, U4
  • jstring: U1, U2, D3
  • kDataPath: D1, U2
  • kInterfacePath: D1, U2, U3
  • native_thread_start: D1, U2

Android Studio:ping URL 时出错:http://google.com不允许到 google.com 的明文 HTTP 流量

Android Studio:ping URL 时出错:http://google.com不允许到 google.com 的明文 HTTP 流量

如何解决Android Studio:ping URL 时出错:http://google.com不允许到 google.com 的明文 HTTP 流量

我正在使用 Facebook Audience Network AdMob 测试 mediation 广告。我正在使用他们文档中提到的 Mediation Test Suite 对其进行测试。

当我尝试从中介测试套件中的 facebook 广告加载广告时出现此错误(从 admob 加载广告工作正常)。

  1. Error while pinging URL: http://google.com. Cleartext HTTP traffic to google.com not permitted
  2. Ad Failed to load : 0

SO 中提出了很多类似的问题,但没有一个对我有用。 这与 Network security configuration 有关。根据他们的文档,我在我的应用程序中添加了所有必要的文件。它们是:

res/xml/network_security_config.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <network-security-config>
  3. <domain-config cleartextTrafficPermitted="true">
  4. <domain includeSubdomains="true">127.0.0.1</domain>
  5. </domain-config>
  6. </network-security-config>

AndroidManifest.xml

  1. <application
  2. ...
  3. android:networkSecurityConfig="@xml/network_security_config">
  4. ...
  5. </application>

我也试过在清单文件中使用 android:usesCleartextTraffic="true" 但后来我得到这个错误:

  1. Ad Failed to load : 0
  2. Received non-success response code 302 from pinging URL: http://google.com

我已经按照 SO 上的一些答案中的建议卸载并安装了我的应用,但仍然无法正常工作。

我该如何解决这个问题?

Android View 的绘制流程之 Layout 和 Draw 过程详解 (二) Android View 绘制流程之 DecorView 与 ViewRootImplAndroid View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)Android View 的事件分发原理解析Android 自定义 View 详解Android View 的绘制流程之 Measure 过程详解 (一)

Android View 的绘制流程之 Layout 和 Draw 过程详解 (二) Android View 绘制流程之 DecorView 与 ViewRootImplAndroid View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)Android View 的事件分发原理解析Android 自定义 View 详解Android View 的绘制流程之 Measure 过程详解 (一)

View 的绘制系列文章:

  • Android View 绘制流程之 DecorView 与 ViewRootImpl

  • Android View 的绘制流程之 Measure 过程详解 (一)

  • Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)

  • Android View 的事件分发原理解析

  • Android 自定义 View 详解

 

在上一篇 Android View 的绘制流程之 Measure 过程详解 (一),已经详细的分析了 DecorView 和其子 View 的测量过程,接下去就要开始讲  layout 和 draw 流程。下面开始进入分析:

DecorView Layout 阶段

在 ViewRootImpl 中,调用 performlayout 方法来确定 DecorView 在屏幕中的位置,下面看下具体的代码逻辑:

// ViewRootImpl 
private void performlayout(WindowManager.LayoutParams lp,int desiredWindowWidth, desiredWindowHeight) { mLayoutRequested = false; mScrollMayChange = true; mInLayout = ; final View host = mView; if (host == null) { return; } if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { Log.v(mTag,"Laying out " + host + " to (" + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); } Trace.traceBegin(Trace.TRACE_TAG_VIEW,"layout"); try {
       // 根据测量结果进行绘制 host.layout(
0,0,host.getMeasuredWidth(),host.getMeasuredHeight()); mInLayout = ; int numViewsRequestingLayout = mLayoutRequesters.size(); if (numViewsRequestingLayout > 0) { // requestLayout() was called during layout. If no layout-request flags are set on the requesting views,there is no problem. If some requests are still pending,then we need to clear those flags and do a full request/measure/layout pass to handle this situation. ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,1)">); if (validLayoutRequesters != ) { Set this flag to indicate that any further requests are happening during the second pass,which may result in posting those requests to the next frame instead mHandlingLayoutInLayoutRequest = ; Process fresh layout requests,then measure and layout int numValidRequests = validLayoutRequesters.size(); for (int i = 0; i < numValidRequests; ++i) { final View view = validLayoutRequesters.get(i); Log.w("View","requestLayout() improperly called by " + view + " during layout: running second layout pass"); view.requestLayout(); } measureHierarchy(host,lp,mView.getContext().getResources(),desiredWindowWidth,desiredWindowHeight); mInLayout = true; host.layout(0,host.getMeasuredHeight()); mHandlingLayoutInLayoutRequest = Check the valid requests again,this time without checking/clearing the layout flags,since requests happening during the second pass get noop''d validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,1)">); ) { final ArrayList<View> finalRequesters = validLayoutRequesters; Post second-pass requests to the next frame getRunQueue().post(new Runnable() { @Override public void run() { finalRequesters.size(); i) { finalRequesters.get(i); Log.w("View","requestLayout() improperly called by " + view + " during second layout pass: posting in next frame"); view.requestLayout(); } } }); } } } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } mInLayout = ; }

当在 layout 绘制过程中,收到了关于重新 layout 的请求,会先判断这些请求里面哪些是有效的,如果是有效的,那么就必须先处理,清除 layout-request flags (View.PFLAG_FORCE_LAYOUT)这些标记,再做一次彻底的重绘工作:重新测量,layout。

那么对于 DecorView 来说,调用 layout 方法,就是对它自身进行布局,注意到传递的参数分别是 0,0, host.getMeasuredWidth,host.getMeasuredHeigh,它们分别代表了一个  View 的上下左右四个位置,显然,DecorView 的左上位置为 0,然后宽高为它的测量宽高,下面来看 layout 的具体代码:

 ViewGroup 
final void layout(int l,1)">int t,1)">int r,1)"> b) {
        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
            if (mTransition != ) {
                mTransition.layoutChange(this);
            }
            super.layout(l,t,r,b);
        } else {
             record the fact that we noop''d it; request layout when transition finishes
            mLayoutCalledWhileSuppressed = ;
        }
    }

 由于 ViewGroup 的 layout 方法是 final 类型,子类不能重写,这里调用了父类的  View#layout 方法,下面看看该方法是如何操作的:

// View   
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) { onMeasure(mOldWidthMeasureSpec,mOldHeightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight;      // 设置界面的显示大小 boolean changed = isLayoutModeOptical(mParent) ? setopticalFrame(l,b) : setFrame(l,b);      // 发生了改变,或者是需要重新 layout,那么就会进入 onLayout 逻辑 if (changed || (mPrivateFlags & PFLAG_LAYOUT_required) == PFLAG_LAYOUT_required) {
// 开始 onLayout(changed,l,b);
if (shouldDrawRoundScrollbar()) { if(mRoundScrollbarRenderer == ) { mRoundScrollbarRenderer = new RoundScrollbarRenderer(); } } { mRoundScrollbarRenderer = ; }        // 清除请求 layout 的标记 mPrivateFlags &= ~PFLAG_LAYOUT_required; ListenerInfo li = mListenerInfo; if (li != null && li.mOnLayoutchangelisteners != ) { ArrayList<OnLayoutchangelistener> listenerscopy = (ArrayList<OnLayoutchangelistener>)li.mOnLayoutchangelisteners.clone(); int numListeners = listenerscopy.size(); int i = 0; i < numListeners; ++i) { listenerscopy.get(i).onLayoutChange(boolean wasLayoutValid = isLayoutValid(); mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_IS_LAID_OUT; if (!wasLayoutValid && isFocused()) { mPrivateFlags &= ~PFLAG_WANTS_FOCUS; (canTakeFocus()) { We have a robust focus,so parents should no longer be wanting focus. clearParentsWantFocus(); } else if (getViewRootImpl() == getViewRootImpl().isInLayout()) { This is a weird case. Most-likely the user,rather than ViewRootImpl,called layout. In this case,there''s no guarantee that parent layouts will be evaluated and thus the safest action is to clear focus here. clearFocusInternal(null,1)">/* propagate */ true,1)"> refocus ); clearParentsWantFocus(); } if (!hasParentWantsFocus()) { original requestFocus was likely on this view directly,so just clear focus clearFocusInternal( otherwise,we let parents handle re-assigning focus during their layout passes. } if ((mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) { mPrivateFlags &= ~PFLAG_WANTS_FOCUS; View focused = findFocus(); if (focused != Try to restore focus as close as possible to our starting focus. if (!restoreDefaultFocus() && !hasParentWantsFocus()) { Give up and clear focus once we''ve reached the top-most parent which wants focus. focused.clearFocusInternal(); } } } if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) { mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT; notifyEnterOrExitForAutoFillIfNeeded(); } }

 调用了 setFrame 方法,并把四个位置信息传递进去,这个方法用于确定 View 的四个顶点的位置,看下具体的代码:

protected boolean setFrame(int left,1)">int top,1)">int right,1)"> bottom) {
        boolean changed =  (DBG) {
            Log.d(VIEW_LOG_TAG,1)">this + " View.setFrame(" + left + "," + top + ","
                    + right + "," + bottom + ")");
        }
     // 有一个不一样说明发生了改变
        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
            changed = ;

             Remember our drawn bit
            int drawn = mPrivateFlags & PFLAG_DRAWN;

            int oldWidth = mRight - mLeft;
            int oldHeight = mBottom - mTop;
            int newWidth = right - left;
            int newHeight = bottom - top;
            boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);

             Invalidate our old position
            invalidate(sizeChanged);

            mLeft = left;
            mTop = top;
            mRight = right;
            mBottom = bottom;
            mRenderNode.setLeftTopRightBottom(mLeft,mTop,mRight,mBottom);
       // 做好标记
            mPrivateFlags |= PFLAG_HAS_BOUNDS;

       // size 改变的回调
             (sizeChanged) {
                sizeChange(newWidth,newHeight,oldWidth,oldHeight);
            }

            if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView !=  If we are visible,force the DRAWN bit to on so that
                 this invalidate will go through (at least to our parent).
                 This is because someone may have invalidated this view
                 before this call to setFrame came in,thereby clearing
                 the DRAWN bit.
                mPrivateFlags |= PFLAG_DRAWN;
                invalidate(sizeChanged);
                 parent display list may need to be recreated based on a change in the bounds
                 of any child
                invalidateParentCaches();
            }

             Reset drawn bit to original value (invalidate turns it off)
            mPrivateFlags |= drawn;

            mBackgroundSizeChanged = ;
            mDefaultFocusHighlightSizeChanged = if (mForegroundInfo != ) {
                mForegroundInfo.mBoundsChanged = ;
            }

            notifySubtreeAccessibilityStateChangedIfNeeded();
        }
         changed;
    }
这里我们看到它对 mLeft、mTop、mRight、mBottom 这四个值进行了重新赋值,对于每一个View,包括 ViewGroup 来说,以上四个值保存了 Viwe 的位置信息,所以这四个值是最终宽高,也即是说,如果要得到 View 的位置信息,那么就应该在 layout 方法完成后调用 getLeft()、getTop() 等方法来取得最终宽高,如果是在此之前调用相应的方法,只能得到 0 的结果。当初始化完毕后,ViewGroup 的布局流程也就完成了。

赋值后,前后对比大小,如果发生了改变,就会调用了 sizeChange()方法,最终会回调 onSizeChanged,标明 view 的尺寸发生了变化,第一次 laout 的时候也会调用。在自定义view 的时候,可以在这里获取控件的宽和高度。

  void sizeChange(int newWidth,1)">int newHeight,1)">int oldWidth,1)"> oldHeight) {
        onSizeChanged(newWidth,oldHeight);
      ......
}

子 View Layout 流程

当 DecorView 确定好了自己的位置之后,开始调用 onLayout 来确定子 view 的位置。对于 onLayout 方法,View 和 ViewGroup 类是空实现,接下来看 FrameLayout 的实现:

 FrameLayout 
void onLayout(boolean changed,1)"> bottom) {
        layoutChildren(left,top,right,bottom,1)">false  no force left gravity */);
    }

 可以看到,该方法调用了 layoutChildren 来确定子 view 的位置。

 FrameLaout   
void layoutChildren(int bottom,1)">boolean forceLeftGravity) { int count = getChildCount();      // 获取 DecoverView 剩余的空间范围 int parentLeft = getPaddingLeftWithForeground(); int parentRight = right - left - getPaddingRightWithForeground(); int parentTop = getPaddingTopWithForeground(); int parentBottom = bottom - top - getPaddingBottomWithForeground(); int i = 0; i < count; i++final View child = getChildAt(i); if (child.getVisibility() != GONE) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); int width = child.getMeasuredWidth(); int height = child.getMeasuredHeight(); childLeft; childTop;
          // DEFAULT_CHILD_GraviTY = Gravity.TOP | Gravity.START 默认是在左上角
int gravity = lp.gravity;
                if (gravity == -1) {
                    gravity = DEFAULT_CHILD_GraviTY;
                }

                int layoutDirection = getLayoutDirection();
                int absoluteGravity = Gravity.getAbsoluteGravity(gravity,layoutDirection);
                int verticalGravity = gravity & Gravity.VERTICAL_GraviTY_MASK;
          // 确定左边起始点
                switch (absoluteGravity & Gravity.HORIZONTAL_GraviTY_MASK) {
                    case Gravity.CENTER_HORIZONTAL:
                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                        lp.leftMargin - lp.rightMargin;
                        break;
                     Gravity.RIGHT:
                        forceLeftGravity) {
                            childLeft = parentRight - width - lp.rightMargin;
                            ;
                        }
                     Gravity.LEFT:
                    default:
                        childLeft = parentLeft + lp.leftMargin;
                }
          // 确定上边起始点
                switch (verticalGravity) {
                     Gravity.TOP:
                        childTop = parentTop + lp.topMargin;
                         Gravity.CENTER_VERTICAL:
                        childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                        lp.topMargin - lp.bottomMargin;
                         Gravity.BottOM:
                        childTop = parentBottom - height -:
                        childTop = parentTop + lp.topMargin;
                }

                child.layout(childLeft,childTop,childLeft + width,childTop + height);
            }
        }
    }

先梳理一下以上逻辑:

  • 首先先获取父容器的 padding 值,得到 DecorView 的可用于显示的空间范围。

  • 然后遍历其每一个子 View,根据子 View 的 layout_gravity 属性、子 View 的测量宽高、父容器的 padding 值、来确定子 View 的左上角的坐标位置

  • 然后调用 child.layout 方法,参数是左上角坐标和自身宽高结合起来的,这样就可以确定子 View 的位置。

最终调用 layout 是 View#layout,前面已经分析过,就不在分析了。

可以看到子 View 的布局流程也很简单,如果子 View 是一个 ViewGroup,那么就会重复以上步骤,如果是一个 View,那么会直接调用 View#layout 方法,根据以上分析,在该方法内部会设置 view 的四个布局参数,接着调用 onLayout 方法 :

 bottom) {

}

此方法是一个空方法,也就是说需要子类去实现此方法,不同的 View 实现方式不同,这里就不分析了。

layout阶段的基本思想也是由根View开始,递归地完成整个控件树的布局(layout)工作。 

对于宽高的获取这里在总结下:

 

 

 这里需要注意一下,在非一般情况下,也就是通过人为设置,重写View的layout()强行设置,这种情况下,测量的值与最终的值是不一样的。

Layout 整体的流程图

上面分别从 View 和 ViewGroup 的角度讲解了布局流程,这里再以流程图的形式归纳一下整个 Layout 过程,便于加深记忆:

 

DecorView Draw 流程

Draw 的入口也是在 ViewRootImpl 中,执行 ViewRootImpl#performTraversals 中会执行 ViewRootIml#performDraw:

 performDraw() {
...
fullRedrawNeeded,它的作用是判断是否需要重新绘制全部视图
draw(fullRedrawNeeded);
...
}

然后会执行到 ViewRootImpl#draw:

void draw( fullRedrawNeeded) {
 ...
 获取mDirty,该值表示需要重绘的区域
 final Rect dirty = mDirty;
 if (mSurfaceHolder != ) {
   The app owns the surface,we won''t draw.
  dirty.setEmpty();
   (animating) {
   if (mScroller != ) {
    mScroller.abortAnimation();
   }
   disposeResizeBuffer();
  }
  ;
 }

 如果fullRedrawNeeded为真,则把dirty区域置为整个屏幕,表示整个视图都需要绘制
 第一次绘制流程,需要绘制所有视图
  (fullRedrawNeeded) {
  mAttachInfo.mIgnoreDirtyState = ;
  dirty.set(0,(int) (mWidth * appScale + 0.5f),1)">int) (mHeight * appScale + 0.5f));
 }
 ...
 drawSoftware(surface,mAttachInfo,xOffset,yOffset,scalingrequired,dirty)) {
    ;
  }
}

 接着会执行到 ViewRootIml#drawSoftware,然后在 ViewRootIml#drawSoftware 会执行到 mView.draw(canvas)。

boolean drawSoftware(Surface surface,AttachInfo attachInfo,1)">int xoff,1)"> yoff,1)"> scalingrequired,Rect dirty) {
 final Canvas canvas;
  锁定canvas区域,由dirty区域决定
  这个canvas就是我们想在上面绘制东西的画布
  canvas = mSurface.lockCanvas(dirty);
  ...
 画布支持位图的密度,和手机分辨率相关
  canvas.setDensity(mDensity);
 ...
   if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                canvas.drawColor(0yoff);
   ...
   正式开始绘制
   mView.draw(canvas);
  ...
 提交需要绘制的东西
  surface.unlockCanvasAndPost(canvas);
}

 mView.draw(canvas) 开始真正的绘制。此处 mView 就是 DecorView,先看 DecorView 中 Draw 的方法:

   draw(Canvas canvas) {
        .draw(canvas);

        if (mMenuBackground != ) {
            mMenuBackground.draw(canvas);
        }
    }

 里面会调用 View#draw:

    int privateFlags = mPrivateFlags;
        boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary,save the canvas'' layers to prepare for fading
         *      3. Draw view''s content
         *      4. Draw children
         *      5. If necessary,draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

         Step 1,draw the background,if needed
         saveCount;

        绘制背景
        dirtyOpaque) {
            drawBackground(canvas);
        }

         如果可以跳过2和5步
        int viewFlags = mViewFlags;
      判断是否有绘制衰退边缘的标示
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
      如果没有绘制衰退边缘只需要3,4,6步
        if (!verticalEdges && !horizontalEdges) {
             Step 3,draw the content
            dirtyOpaque) onDraw(canvas);

             Step 4,draw the children
            dispatchDraw(canvas);

             Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getoverlayView().dispatchDraw(canvas);
            }

             Step 6,draw decorations (foreground,scrollbars)
            onDrawForeground(canvas);

             we''re done...
            ;
        }

        
         * Here we do the full fledged routine...
         * (this is an uncommon case where speed matters less,* this is why we repeat some of the tests that have been
         * done above)
         boolean drawTop = boolean drawBottom = boolean drawLeft = boolean drawRight = float topFadeStrength = 0.0ffloat bottomFadeStrength = 0.0ffloat leftFadeStrength = 0.0ffloat rightFadeStrength = 0.0f Step 2,save the canvas'' layers
        int paddingLeft = mPaddingLeft;

        boolean offsetrequired = isPaddingOffsetrequired();
         (offsetrequired) {
            paddingLeft += getLeftPaddingOffset();
        }

        int left = mScrollX + paddingLeft;
        int right = left + mRight - mLeft - mPaddingRight -int top = mScrollY + getFadetop(offsetrequired);
        int bottom = top + getFadeHeight(offsetrequired);

         (offsetrequired) {
            right += getRightPaddingOffset();
            bottom += getBottomPaddingOffset();
        }

        final ScrollabilityCache scrollabilityCache = mScrollCache;
        float fadeHeight = scrollabilityCache.fadingEdgeLength;
        int length = () fadeHeight;

         clip the fade length if top and bottom fades overlap
         overlapping fades produce odd-looking artifacts
        if (verticalEdges && (top + length > bottom - length)) {
            length = (bottom - top) / 2 also clip horizontal fades if necessary
        if (horizontalEdges && (left + length > right - length)) {
            length = (right - left) / 2 (verticalEdges) {
            topFadeStrength = Math.max(0.0f,Math.min(1.0f;
            bottomFadeStrength = Math.max(0.0f,getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0f (horizontalEdges) {
            leftFadeStrength = Math.max(0.0f,getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0f;
            rightFadeStrength = Math.max(0.0f,getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0f;
        }

        saveCount = canvas.getSaveCount();

        int solidColor = getSolidColor();
        if (solidColor == 0int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

             (drawTop) {
                canvas.saveLayer(left,top + length,flags);
            }

             (drawBottom) {
                canvas.saveLayer(left,bottom - length,1)"> (drawLeft) {
                canvas.saveLayer(left,left + length,1)"> (drawRight) {
                canvas.saveLayer(right - length,flags);
            }
        }  {
            scrollabilityCache.setFadeColor(solidColor);
        }

        dirtyOpaque) onDraw(canvas);

                dispatchDraw(canvas);

         Step 5,draw the fade effect and restore layers
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;

         (drawTop) {
            matrix.setScale(1,fadeHeight * topFadeStrength);
            matrix.postTranslate(left,top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left,top + length,p);
        }

         (drawBottom) {
            matrix.setScale(1,1)"> bottomFadeStrength);
            matrix.postRotate(180);
            matrix.postTranslate(left,bottom);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left,bottom - (drawLeft) {
            matrix.setScale(1,1)"> leftFadeStrength);
            matrix.postRotate(-90 (drawRight) {
            matrix.setScale(1,1)"> rightFadeStrength);
            matrix.postRotate(90);
            matrix.postTranslate(right,top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(right -etoCount(saveCount);

         Overlay is part of the content and draws beneath Foreground
        mOverlay.isEmpty()) {
            mOverlay.getoverlayView().dispatchDraw(canvas);
        }

                onDrawForeground(canvas);
    }

可以看到,draw过程比较复杂,但是逻辑十分清晰,而官方注释也清楚地说明了每一步的做法。我们首先来看一开始的标记位 dirtyOpaque,该标记位的作用是判断当前 View 是否是透明的,如果 View 是透明的,那么根据下面的逻辑可以看出,将不会执行一些步骤,比如绘制背景、绘制内容等。这样很容易理解,因为一个 View 既然是透明的,那就没必要绘制它了。接着是绘制流程的六个步骤,这里先小结这六个步骤分别是什么,然后再展开来讲。绘制流程的六个步骤:

  1. 对 View 的背景进行绘制

  2. 保存当前的图层信息(可跳过)

  3. 绘制 View 的内容

  4. 对 View 的子 View 进行绘制(如果有子 View )

  5. 绘制 View 的褪色的边缘,类似于阴影效果(可跳过)

  6. 绘制 View 的装饰(例如:滚动条)

其中第2步和第5步是可以跳过的,我们这里不做分析,我们重点来分析其它步骤。

ViewGroup子类默认情况下就是不执行 onDraw 方法的,在 ViewGroup 源码中的 initViewGroup() 方法中设置了一个标记,源码如下:

 initViewGroup() {
         ViewGroup doesn''t draw by default
        debugDraw()) {
            setFlags(WILL_NOT_DRAW,DRAW_MASK);
        }
        ......
}

看第二行注释也知道,ViewGroup 默认情况下是不会 draw 的。第四行调用 setFlags 方法设置标记 WILL_NOT_DRAW,在回到 View 中 draw 方法看第2行代码:

1   mPrivateFlags;
2  boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
3      (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);

 

setFlags 方法就是对 View中mPrivateFlags 值进行相应改变,我们设置标记 WILL_NOT_DRAW 那么 dirtyOpaque 得到的值就为 true,从而 if (!dirtyOpaque) 不成立,也就不会执行onDraw 方法。

1. 绘制背景

View#drawBackground

     drawBackground(Canvas canvas) {
       获取背景的Drawable,没有就不需要绘制
        final Drawable background = mBackground;
        if (background == ;
        }
       确定背景Drawable边界
        setBackgroundBounds();
        ...

       如果有偏移量先偏移画布再将drawable绘制上去
        int scrollX = mScrollX;
        int scrollY = mScrollY;
        if ((scrollX | scrollY) == 0) {
            background.draw(canvas);
        }  {
            canvas.translate(scrollX,scrollY);
            此处会执行各种Drawable对应的draw方法
            background.draw(canvas);
            把画布的原点移回去,drawable在屏幕上的位置不动
            canvas.translate(-scrollX,1)">scrollY);
        }
    }

3. 绘制 View 的内容

先跳过第 2 步,是因为不是所有的 View 都需绘制褪色边缘。DecorView#onDraw:

    onDraw(Canvas c) {
        .onDraw(c);

        mBackgroundFallback.draw(
   onDraw(Canvas canvas) {
}

onDraw 是空实现,需要子 View 自己去绘制。对于DecorView 一般也没啥内容,除了需要背景颜色等,所以本身并需要绘制啥。

4. 绘制子View

DecorView 绘制完成后,开始绘制子 View,所以 ViewGroup 的绘制需要绘制子 View,直接看看 ViewGroup#dispatchDraw:

 dispatchDraw(Canvas canvas) {
       boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
       int childrenCount = mChildrenCount;
       final View[] children = mChildren;
        mGroupFlags;

ViewGroup是否有设置子View入场动画,如果有绑定到View
 启动动画控制器
      ...

指定修改区域
       int clipSaveCount = 0;
       boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
    // 不让子view绘制在pandding里面,也就是去除padding
(clipToPadding) { clipSaveCount = canvas.save(); canvas.clipRect(mScrollX + mPaddingLeft,mScrollY + mPaddingTop,mScrollX + mRight - mLeft - mPaddingRight,mScrollY + mBottom - mTop - mPaddingBottom); } ... int i = 0; i < childrenCount; i++) { 先取mTransientViews中的View,mTransientViews中的View通过addTransientView添加,它们只是容器渲染的一个item while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) { final View transientChild = mTransientViews.get(transientIndex); if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || transientChild.getAnimation() != ) { more |= drawChild(canvas,transientChild,drawingTime); } transientIndex++; if (transientIndex >= transientCount) { transientIndex = -1; } } int childindex = customOrder ? getChildDrawingOrder(childrenCount,i) : i; final View child = (preorderedList == ) ? children[childindex] : preorderedList.get(childindex); if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != ) { more |=dispatchDraw 的流程是先启动第一次加到布局中的动画,然后确定绘制区域,遍历绘制 View,遍历 View 的时候优先绘制渲染的 mTransientViews,绘制 View 调用到ViewGroup#drawChild:

boolean drawChild(Canvas canvas,View child,1)">long drawingTime) {
        View中有两个draw方法
        这个多参数的draw用于view绘制自身内容
        return child.draw(canvas,drawingTime);
    }

 View#draw(canvas,this,drawingTime)

boolean draw(Canvas canvas,ViewGroup parent,1)"> drawingTime) {
 
       boolean drawingWithRenderNode = mAttachInfo != null
                && mAttachInfo.mHardwareAccelerated
                && hardwareAcceleratedCanvas;
      ...

     主要判断是否有绘制缓存,如果有,直接使用缓存,如果没有,调用 draw(canvas)方法
        drawingWithDrawingCache) {
             (drawingWithRenderNode) {
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                ((displayListCanvas) canvas).draWrenderNode(renderNode);
            }  {
                 Fast path for layouts with no backgrounds
                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                    dispatchDraw(canvas);
                }  {
                    draw(canvas);
                }
            }
        } if (cache != PFLAG_DIRTY_MASK;
            if (layerType == LAYER_TYPE_NONE) {
                 no layer paint,use temporary paint to draw bitmap
                Paint cachePaint = parent.mCachePaint;
                if (cachePaint == ) {
                    cachePaint =  Paint();
                    cachePaint.setDither();
                    parent.mCachePaint = cachePaint;
                }
                cachePaint.setAlpha((int) (alpha * 255));
                canvas.drawBitmap(cache,0.0f,0.0f use layer paint to draw the bitmap,merging the two alphas,but also restore
                int layerPaintAlpha = mLayerPaint.getAlpha();
                mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
                canvas.drawBitmap(cache,mLayerPaint);
                mLayerPaint.setAlpha(layerPaintAlpha);
            }
      }
}

首先判断是否已经有缓存,即之前是否已经绘制过一次了,如果没有,则会调用 draw(canvas) 方法,开始正常的绘制,即上面所说的六个步骤,否则利用缓存来显示。

这一步也可以归纳为 ViewGroup 绘制过程,它对子 View 进行了绘制,而子 View 又会调用自身的 draw 方法来绘制自身,这样不断遍历子 View 及子 View 的不断对自身的绘制,从而使得 View 树完成绘制。

对于自定义 View ,如果需要绘制东西的话,直接重新 onDraw 就可以了。

6. 绘制装饰

     onDrawForeground(Canvas canvas) {
     绘制滑动指示
        onDrawScrollIndicators(canvas);
     绘制ScrollBar
        onDrawScrollBars(canvas);
     获取前景色的Drawable,绘制到canvas上
        final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : if (foreground !=  (mForegroundInfo.mBoundsChanged) {
                mForegroundInfo.mBoundsChanged = ;
                final Rect selfBounds = mForegroundInfo.mSelfBounds;
                final Rect overlayBounds = mForegroundInfo.mOverlayBounds;
                 (mForegroundInfo.mInsidePadding) {
                    selfBounds.set(0,0 {
                    selfBounds.set(getPaddingLeft(),getPaddingTop(),getWidth() - getPaddingRight(),getHeight() - getPaddingBottom());
                }
                int ld = getLayoutDirection();
                Gravity.apply(mForegroundInfo.mGravity,foreground.getIntrinsicWidth(),foreground.getIntrinsicHeight(),selfBounds,overlayBounds,ld);
                foreground.setBounds(overlayBounds);
            }
            foreground.draw(canvas);
        }
    }

 2和5.绘制View的褪色边缘

当 horizontalEdges 或者 verticalEdges 有一个 true 的时候,表示需要绘制 View 的褪色边缘:

     boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;

这时候先计算出是否需要绘制上下左右的褪色边缘和它的参数,然后保存视图层:

         mPaddingLeft;
         getLeftPaddingOffset();
        }
         getFadeHeight(offsetrequired);
         getBottomPaddingOffset();
        }
        ) fadeHeight;
        ;
        }
        saveCount = canvas.getSaveCount();
         Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
             {
            scrollabilityCache.setFadeColor(solidColor);
        }

绘制褪色边缘,恢复视图层 :

         scrollabilityCache.shader;
        etoCount(saveCount);

所谓的绘制装饰,就是指 View 除了背景、内容、子 View 的其余部分,例如滚动条等。

最后附上 View 的 draw 流程:

 

 

到此,View 的绘制流程就讲完了,下一篇会讲自定义 View。

小结:

  • view 不停找 parent 可以一直找到 DecorView,按理说 DecorView 是顶点了,但是 DecorView 还有个虚拟父 view,ViewRootImpl。 ViewRootImpl 不是一个 View 或者ViewGroup,他有个成员 mView 是 DecorView,所有的操作从 ViewRootImpl 开始自上而下分发

  • view 的 invalidate 不会导致 ViewRootImpl 的 invalidate 被调用,而是递归调用父 view的invalidateChildInParent,直到 ViewRootImpl 的 invalidateChildInParent,然后触发peformTraversals,会导致当前 view 被重绘,由于 mLayoutRequested 为 false,不会导致 onMeasure 和 onLayout 被调用,而 OnDraw 会被调用

  • 一个 view 的 invalidate 会导致本身 PFLAG_INVALIDATED 置 1,导致本身以及父族 viewgroup 的 PFLAG_DRAWING_CACHE_VALID 置 0

  • requestLayout 会直接递归调用父窗口的 requestLayout,直到 ViewRootImpl,然后触发 peformTraversals,由于 mLayoutRequested 为 true,会导致 onMeasure 和onLayout 被调用。不一定会触发 OnDraw

  • requestLayout 触发 onDraw 可能是因为在在 layout 过程中发现 l,b 和以前不一样,那就会触发一次 invalidate,所以触发了onDraw,也可能是因为别的原因导致 mDirty 非空(比如在跑动画)

  • requestLayout 会导致自己以及父族 view 的 PFLAG_FORCE_LAYOUT 和 PFLAG_INVALIDATED 标志被设置。

  • 一般来说,只要刷新的时候就调用 invalidate,需要重新 measure 就调用 requestLayout,后面再跟个 invalidate(为了保证重绘),

 

总结

以上是小编为你收集整理的Android View 的绘制流程之 Layout 和 Draw 过程详解 (二) Android View 绘制流程之 DecorView 与 ViewRootImplAndroid View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)Android View 的事件分发原理解析Android 自定义 View 详解Android View 的绘制流程之 Measure 过程详解 (一)全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

我们今天的关于android http 缓存android缓存数据的分享就到这里,谢谢您的阅读,如果想了解更多关于android import android-async-http-master有错误、android jni 回调 转自 http://android.wooyd.org/JNIExa...、Android Studio:ping URL 时出错:http://google.com不允许到 google.com 的明文 HTTP 流量、Android View 的绘制流程之 Layout 和 Draw 过程详解 (二) Android View 绘制流程之 DecorView 与 ViewRootImplAndroid View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)Android View 的事件分发原理解析Android 自定义 View 详解Android View 的绘制流程之 Measure 过程详解 (一)的相关信息,可以在本站进行搜索。

本文标签: