Android基础第七天

Android基础第七天

多线程下载原理

多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序“拼接”起来就构成了完整的文件了。这样就大大提高了文件的下载效率。

多线程下载大致可分为以下几个步骤:

一.  获取服务器上的目标文件的大小

显然这一步是需要先访问一下网络,只需要获取到目标文件的总大小即可。目的是为了计算每个线程应该分配的下载任务。

二.  在本地创建一个跟原始文件同样大小的文件

在本地可以通过RandomAccessFile创建一个跟目标文件同样大小的文件,该api 支持文件任意位置的读写操作。这样就给多线程下载提供了方便,每个线程只需在指定起始和结束脚标范围内写数据即可。

三.  计算每个线程下载的起始位置和结束位置

我们可以把原始文件当成一个字节数组,每个线程只下载该“数组”的指定起始位置和指定结束位置之间的部分。在第一步中我们已经知道了“数组”的总长度。因此只要再知道总共开启的线程的个数就好计算每个线程要下载的范围了。

每个线程需要下载的字节个数(blockSize)=总字节数(totalSize)/线程数(threadCount)。

假设给线程按照0,1,2,3...n 的方式依次进行编号,那么第n个线程下载文件的范围为:

起始脚标startIndex=n*blockSize。

结束脚标endIndex=(n-1)*blockSize-1。

考虑到totalSize/threadCount不一定能整除,因此对已最后一个线程应该特殊处理,最后一个线程的起始脚标计算公式不变,但是结束脚标endIndex=totalSize-1;即可。

四. 开启多个子线程开始下载

五. 记录下载进度

为每一个单独的线程创建一个临时文件,用于记录该线程下载的进度。对于单独的一个线程,每下载一部分数据就在本地文件中记录下当前下载的字节数。这样子如果下载任务异常终止了,那么下次重新开始下载时就可以接着上次的进度下载。

六. 删除临时文件

当多个线程都下载完成之后,最后一个下载完的线程将所有的临时文件删除。

JavaSE实现多线程下载

代码实现:

创建一个MultiDownLoader的java类,实现main函数,在main函数中实现下载的步骤:

/**

 * 多线程下载器

 */

publicclass MultiDownloader {

    /**

     * 开启几个线程从服务器下载数据

     */

    publicstaticintthreadCount = 3;

 

    publicstaticintrunningThreadCount;

    // 服务器的文件,准备出来,tomcat服务器上.

    publicstatic String path = "http://192.168.1.104:8080/setup.exe";

 

    // 多线程下载

    publicstaticvoid main(String[] args) throws Exception {

       // 1.请求服务器获取到服务器的资源大小

       URL url = new URL(path);

       HttpURLConnection conn =(HttpURLConnection) url.openConnection();

       conn.setConnectTimeout(5000);

       conn.setRequestMethod("GET");

       int code = conn.getResponseCode();

       if (code == 200) {

           int length = conn.getContentLength();

           System.out.println("服务器文件的长度为:" + length);

           // 2.在本地创建一个与服务器资源文件大小相同的资源

           RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rw");

           raf.setLength(length);

           raf.close();

           // 3.计算出每个线程的开始下载位置和结束位置

           int blocksize = length / threadCount;

           runningThreadCount = threadCount;

           for (int threadId = 0; threadId < threadCount; threadId++) {

              int startIndex = threadId * blocksize;

              int endIndex = (threadId + 1) *blocksize - 1;

              if (threadId == (threadCount - 1)) {

                  endIndex = length -1;

              }

              // 开启多个子线程开始下载

              new DownloadThread(threadId,startIndex, endIndex).start();

           }

       }

    }

 

    privatestaticclass DownloadThread extends Thread {

       /**

        * 线程id

        */

       privateintthreadId;

       /**

        * 线程下载的理论开始位置

        */

       privateintstartIndex;

       /**

        * 线程下载的结束位置

        */

       privateintendIndex;

       /**

        * 当前线程下载到文件的那一个位置了.

        */

       privateintcurrentPosition;

 

       public DownloadThread(int threadId, int startIndex, int endIndex) {

           this.threadId = threadId;

           this.startIndex = startIndex;

           this.endIndex = endIndex;

           System.out.println(threadId + "号线程下载的范围为:" + startIndex+ "   ~~  " + endIndex);

           currentPosition = startIndex;

       }

 

       @Override

       publicvoid run() {

           try {

              // 开始从网上下载自己需要下载的那部分资源

              URL url = new URL(path);

              HttpURLConnection conn= (HttpURLConnection) url

                     .openConnection();

              // 检查当前线程是否已经下载过一部分的数据了

              File info = new File(threadId + ".position");

              RandomAccessFile raf = new RandomAccessFile(getFileName(path),

                     "rw");

              if (info.exists() &&info.length() > 0) {

                  FileInputStream fis = new FileInputStream(info);

                  BufferedReader br =new BufferedReader(

                         new InputStreamReader(fis));

                  currentPosition = Integer.valueOf(br.readLine());

                  conn.setRequestProperty("Range", "bytes=" + currentPosition+ "-" + endIndex);

                  System.out.println("原来有下载进度,从上一次终止的位置继续下载" + "bytes="+ currentPosition + "-" + endIndex);

                  fis.close();

                  raf.seek(currentPosition);// 每个线程写文件的开始位置都是不一样的.

              } else {

                  // 以前没有下载----告诉服务器只想下载资源的一部分

                  conn.setRequestProperty("Range", "bytes=" + startIndex+ "-" + endIndex);

                  System.out.println("原来没有有下载进度,新的下载" + "bytes=" + startIndex+ "-" + endIndex);

                  raf.seek(startIndex);// 每个线程写文件的开始位置都是不一样的.

              }

              InputStream is =conn.getInputStream();

              byte[] buffer = newbyte[1];

              int len = -1;

              while ((len = is.read(buffer)) != -1) {

                  // 把每个线程下载的数据放在自己对应下载的文件里面.

                  // System.out.println("线程:"+threadId+"正在下载:"+new

                  // String(buffer));

                  raf.write(buffer,0, len);

                  // 5.记录下载的进度

                  currentPosition += len;

                  File file = new File(threadId + ".position");

                  RandomAccessFilefos = new RandomAccessFile(file, "rwd");

                  // System.out.println("线程:"+threadId+"写到了"+currentPosition);

                  fos.write(String.valueOf(currentPosition).getBytes());

                  fos.close();// fileoutstream数据是不一定被写入到底层设备里面的,有可能是存储在缓存里面.

                  // raf rwd模式,数据是立刻被存储到底层硬盘设备里面.

              }

              raf.close();

              is.close();

              System.out.println("线程:" + threadId + "下载完毕了...");

              File f = new File(threadId + ".position");

              f.renameTo(new File(threadId + ".position.finish"));

              // 6.删除临时文件

              synchronized (MultiDownloader.class) {

                  runningThreadCount--;

                  if (runningThreadCount <= 0) {

                     for (int i = 0; i < threadCount; i++) {

                         File ft = new File(i + ".position.finish");

                         ft.delete();

                     }

                  }

              }

           } catch (Exception e) {

              e.printStackTrace();

           }

       }

    }

 

    /**

     * 获取一个文件名称

     *

     * @param path

     * @return

     */

    publicstatic String getFileName(String path) {

       int start = path.lastIndexOf("/") + 1;

       return path.substring(start);

    }

}

新技能:

1、多线程下载文件的请求属性

如果我们想请求服务器上某文件的一部分,而不是将整个文件都下载下来,那么就必须设置如下属性:connection.setRequestProperty("Range","bytes="+currentIndex+"-"+endIndex);

2、请求部分文件返回成功的状态码是206

当我们请求部分文件成功后,服务器返回的状态码不是200,而是206。

Android 实现多线程下载

将上面JavaSE 实现多线程下载的代码经过一定的改造,便可移植到Android 上。有所不同的是Android有界面可以跟用户进行良好的交互,在界面上让用户输入原文件地址、线程个数,然后点击确定开始下载。为了让用户可以清晰的看到每个线程下载的进度根据线程个数动态的生成等量的进度条(ProgressBar)。

 

 

ProgressBar 的使用

ProgressBar 是一个进度条控件,用于显示一项任务的完成进度。其有两种样式,一种是圆形的该种样式是系统默认的,由于无法显示具体的进度值,适合用于不确定要等待多久的情形下;另一种是长条形的, ,此类进度条有两种颜色,高亮颜色代表任务完成的总进度。对于我们下载任务来说,由于总任务(要下载的字节数)是明确的,当前已经完成的任务(已经下载的字节数)也是明确的,因此特别适合使用后者。

由于在我们的需求里ProgressBar 是需要根据线程的个数动态添加的,而且要求是长条形的。因此可以事先在布局文件中编写好ProgressBar 的样式。当需要用到的时候再将该布局填充起来。

ProgressBar 的max 属性代表其最大刻度值,progress属性代表当前进度值。使用方法如下:

ProgressBar.setMax(int max);设置最大刻度值。

ProgressBar.setProgress(int progress);设置当前进度值。

给ProgressBar 设置最大刻度值和修改进度值可以在子线程中操作的,其内部已经特殊处理过了,因此不需要再通过handler发送Message 让主线程修改进度。

布局编写

多线程下载界面布局如下:

<LinearLayout 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:orientation="vertical"

    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=".MainActivity">

 

    <EditText

        android:id="@+id/et_path"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:hint="请输入要下载的文件资源路径"

        android:text="http://192.168.1.104:8080/gg.exe"/>

 

    <Button

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:onClick="download"

        android:text="下载" />

 

    <ProgressBar

        android:id="@+id/pb0"

        style="?android:attr/progressBarStyleHorizontal"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"/>

 

    <ProgressBar

        android:id="@+id/pb1"

        style="?android:attr/progressBarStyleHorizontal"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"/>

 

    <ProgressBar

        android:id="@+id/pb2"

        style="?android:attr/progressBarStyleHorizontal"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"/>

</LinearLayout>

代码编写

publicclass MainActivity extends Activity {

    private EditText et_path;

    private ProgressBar pb0;

    private ProgressBar pb1;

    private ProgressBar pb2;

    /**

     * 开启几个线程从服务器下载数据

     */

    publicstaticintthreadCount = 3;

 

    publicstaticintrunningThreadCount;

    private String path;

 

    @Override

    protectedvoid onCreate(Bundle savedInstanceState){

       super.onCreate(savedInstanceState);

       setContentView(R.layout.activity_main);

       //初始化控件

       et_path = (EditText) findViewById(R.id.et_path);

       pb0 = (ProgressBar) findViewById(R.id.pb0);

       pb1 = (ProgressBar) findViewById(R.id.pb1);

       pb2 = (ProgressBar) findViewById(R.id.pb2);

    }

 

    //下载按钮的点击事件

    publicvoid download(View view) {

       path = et_path.getText().toString().trim();

       if (TextUtils.isEmpty(path) || (!path.startsWith("http://"))) {

           Toast.makeText(this, "对不起路径不合法", 0).show();

           return;

       }

       new Thread(){

           publicvoid run() {

              try {

                  //1.获取服务器上的目标文件的大小

                  URL url = new URL(path);

                  HttpURLConnectionconn = (HttpURLConnection) url.openConnection();

                  conn.setConnectTimeout(5000);

                  conn.setRequestMethod("GET");

                  int code = conn.getResponseCode();

                  if (code == 200) {

                     int length = conn.getContentLength();

                     System.out.println("服务器文件的长度为:" + length);

                     //2.在本地创建一个跟原始文件同样大小的文件

                     RandomAccessFileraf = new RandomAccessFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+getFileName(path), "rw");

                     raf.setLength(length);

                     raf.close();

                     //3.计算每个线程下载的起始位置和结束位置

                     int blocksize = length / threadCount;

                     runningThreadCount = threadCount;

                     for (int threadId = 0; threadId < threadCount; threadId++) {

                         int startIndex = threadId * blocksize;

                         int endIndex = (threadId + 1) *blocksize - 1;

                         if (threadId == (threadCount - 1)) {

                            endIndex= length - 1;

                         }

                         //4.开启多个子线程开始下载

                         new DownloadThread(threadId,startIndex, endIndex).start();

                     }

                  }

              } catch (Exception e) {

                  e.printStackTrace();

              }

           };

       }.start();

    }

 

   

    privateclass DownloadThread extends Thread {

       /**

        * 线程id

        */

       privateintthreadId;

       /**

        * 线程下载的理论开始位置

        */

       privateintstartIndex;

       /**

        * 线程下载的结束位置

        */

       privateintendIndex;

       /**

        * 当前线程下载到文件的那一个位置了.

        */

       privateintcurrentPosition;

 

       public DownloadThread(int threadId, int startIndex, int endIndex) {

           this.threadId = threadId;

           this.startIndex = startIndex;

           this.endIndex = endIndex;

           System.out.println(threadId + "号线程下载的范围为:" + startIndex

                  + "   ~~  " + endIndex);

           currentPosition = startIndex;

       }

 

       @Override

       publicvoid run() {

           try {

              URL url = new URL(path);

              HttpURLConnection conn= (HttpURLConnection) url.openConnection();

              //检查当前线程是否已经下载过一部分的数据了

              File info = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");

              RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+getFileName(path), "rw");

              if(info.exists()&&info.length()>0){

                  FileInputStream fis= new FileInputStream(info);

                  BufferedReader br =new BufferedReader(new InputStreamReader(fis));

                  currentPosition = Integer.valueOf(br.readLine());

                  conn.setRequestProperty("Range", "bytes="+currentPosition+"-"+endIndex);

                  System.out.println("原来有下载进度,从上一次终止的位置继续下载"+"bytes="+currentPosition+"-"+endIndex);

                  fis.close();

                  raf.seek(currentPosition);//每个线程写文件的开始位置都是不一样的.

              }else{

              //告诉服务器只想下载资源的一部分

                  conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);

                  System.out.println("原来没有有下载进度,新的下载"+ "bytes="+startIndex+"-"+endIndex);

                  raf.seek(startIndex);//每个线程写文件的开始位置都是不一样的.

              }

              InputStream is =conn.getInputStream();

              byte[] buffer = newbyte[1024];

              int len = -1;

              while((len = is.read(buffer))!=-1){

                  //把每个线程下载的数据放在自己的空间里面.

//                System.out.println("线程:"+threadId+"正在下载:"+new String(buffer));

                  raf.write(buffer,0,len);

                  //5.记录下载进度

                  currentPosition+=len;

                  File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");

                  RandomAccessFilefos = new RandomAccessFile(file,"rwd");

                  //System.out.println("线程:"+threadId+"写到了"+currentPosition);

                  fos.write(String.valueOf(currentPosition).getBytes());

                  fos.close();//fileoutstream数据是不一定被写入到底层设备里面的,有可能是存储在缓存里面.

                  //raf rwd模式,数据是立刻被存储到底层硬盘设备里面.

                 

                  //更新进度条的显示

                  int max = endIndex - startIndex;

                  int progress = currentPosition - startIndex;

                  if(threadId==0){

                     pb0.setMax(max);

                     pb0.setProgress(progress);

                  }elseif(threadId==1){

                     pb1.setMax(max);

                     pb1.setProgress(progress);

                  }elseif(threadId==2){

                     pb2.setMax(max);

                      pb2.setProgress(progress);

                  }

              }

              raf.close();

              is.close();

              System.out.println("线程:"+threadId+"下载完毕了...");

              File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");

              f.renameTo(new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position.finish"));

              synchronized (MainActivity.class) {

                  runningThreadCount--;

                  //6.删除临时文件

                  if(runningThreadCount<=0){

                     for(int i=0;i<threadCount;i++){

                         File ft = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+i+".position.finish");

                         ft.delete();

                     }

                  }

              }

           } catch (Exception e) {

              e.printStackTrace();

           }

       }

    }

    /**

     * 获取一个文件名称

     * @param path

     * @return

     */

    public String getFileName(String path){

       int start = path.lastIndexOf("/")+1;

       return path.substring(start);

    }

}

添加权限

在该工程中不仅用到了网络访问还用到了sdcard 存储,因此需要添加两个权限。

    <uses-permission android:name="android.permission.INTERNET"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

xUtils 实现多线程下载

xUtils 简介

xUtils 是开源免费的Android 工具包,代码托管在github上。目前xUtils 主要有四大模块:

l  DbUtils 模块

操作数据库的框架。

l  ViewUtils 模块

通过注解的方式可以对UI,资源和事件绑定进行管理。

l  HttpUtils 模块

提供了方便的网络访问,断点续传等功能。

l  BitmapUtils 模块

提供了强大的图片处理工具。

我们在这里只简单实用xUtils 工具中的HttpUtils 工具。xUtils 的下载地址:

https://github.com/wyouflf/xUtils

点击绿色按钮可以下载library库和sample示例代码进行学习和开发。在github网页上的下方也有xutils对里面的每一种模块进行的详细介绍。

 

xUtils 之HttpUtils 的使用

将下载好的zip 包解压,然后将src 下的源代码拷贝在自己工程中如图:

或者直接拷贝xUtils的jar包到libs目录下:

接下来就可以使用xUtils中的httpUtils的功能了:

使用HttpUtils 完成文件下载

HttpUtils http = new HttpUtils();

       /**

        * 参数1:原文件网络地址

        * 参数2:本地保存的地址

        * 参数3:是否支持断点续传,true:支持,false:不支持

        * 参数4:回调接口,该接口中的方法都是在主线程中被调用的,

        * 也就是该接口中的方法都可以修改UI

        */

       http.download(path, "/mnt/sdcard/xxx.exe", true, new RequestCallBack<File>() {

          

           //下载成功后调用一次

           @Override

           publicvoid onSuccess(ResponseInfo<File>arg0) {

              Toast.makeText(MainActivity.this, "下载成功", 0).show();

           }

          

           /**

            * 每下载一部分就被调用一次,通过该方法可以知道前下载进度

            * 参数1:原文件总字节数

            * 参数2:当前已经下载好的字节数

            * 参数3:是否在上传,对于下载,该值为false

            */

           @Override

           publicvoid onLoading(long total, long current, boolean isUploading) {

              pb0.setMax((int) total);

              pb0.setProgress((int) current);

              super.onLoading(total, current,isUploading);

           }

 

           //失败后调用一次

           @Override

           publicvoid onFailure(HttpException arg0,String arg1) {

              Toast.makeText(MainActivity.this, "下载失败"+arg1, 0).show();

           }

       });

 

 

多线程下载

多线程并发操作

网络请求

 

为什么多线程可以提高下载的速度

1.从服务器上获取的资源变多了,单位时间内下载的速度就变快.

2.下载速度还受到服务器上传带宽和用户的下载带宽限制

 

activity的创建步骤

1.写一个类继承Activity.重写onCreate方法

         2.在清单文件的application节点下面配置<activity>Android:name属性

         3.创建布局文件res/layout

         4.onCreate设置布局setContentView(R.layout.);

 

<item name="android:windowNoTitle">true</item>设置无标题

 

activity的跳转:

1.创建意图对象 newIntent();

         2.设置意图的跳转方向intent.setClass(context,OtherActivity.Class)

         3.开启意图startActivity(intent);

         4.关闭当前的Activityfinish();

 

Activity的创建

Activity 是Android 四大组件之一,用于展示界面。Activity 中所有操作都与用户密切相关,是一个负责与用户交互的组件,它上面可以显示一些控件也可以监听并处理用户的事件。一个Activity 通常就是一个单独的屏幕,Activity 之间通过Intent 进行通信。

1.    自定义类继承Activity

新建一个Android 工程,在src 目录下新建一个类,不妨起名MyActivity,让该类继承AndroidSDK提供的Activity。

2.    在MyActivity 中覆写onCreate()方法

onCreate() 方法是在当前Activity被系统创建的时候调用的,在该方法中一般要通过setContentView(R.layout.myactivity)方法给当前Activity 绑定布局文件或视图。因此为了让我们的MyActivity 能够展示界面,需要在工程目录的res/layout/目录下创建一个布局文件。比如我创建的布局文件名(注意:Android 的资源文件名中是不允许有大写字母的,必须全小写,如果有多个单词可以用下划线分开)为myactivity.xml:

<?xml version="1.0"encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

   

    <TextView android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="这是MyActivity的界面"/>

</LinearLayout>

必须要注意的就是在该方法中必须先调用父类的onCreate()方法, 也就是super.onCreate(savedInstanceState);该句代码必须被调用一次。这是为什么呢?我们看看父类的onCreate()方法里面都干了些啥就知道了。如下图所示,在父类的方法中执行了一堆业务逻辑,而且在最后一行用了一个成员变量mCalled 记录了当前方法是否被调用了。虽然我们现在还看不懂这些代码,但是我们已经知道既然父类帮我们实现了这些功能,那么我们子类就一定必须调用一次父类的该方法。

3.    在AndroidManifest.xml中进行Activity 的注册

Android 中共有四大组件,除了BroadCastReceiver之外,其他三个组件如果要使用那么必须在AndroidManifest.xml 中进行注册(也叫声明)。

我们在上面创建的MyActivity 如何注册呢?其实如果你不会的话完全可以将eclipse 自动生成的MainActivity 的注册代码给拷贝过来,然后将android:name 的属性值修改成我们的类。

上面用到的标签以及属性含义如下:

l activity/android:name:要注册的Activity的全限定类名,在eclipse 中按住Ctrl+鼠标左键可以点击到该类的代码视图,必须指定

l activity/android:label:Activity 左上角显示的名称

l  intent-filter:叫意图过滤器,一般用于隐式启动该Activity

l  action/android:name:意图过滤器的Action 名称,可以自定义也可以使用系统提供的。

l  category/android:name:意图过滤器的category 名称,只能使用系统提供的常量值。

 

我将MainActivity 的所有东西都拷贝过来了,只不过是替换了类名。

对于MainActivity来说正是配置了特定(

action 为android:name="android.intent.action.MAIN",

category为android:name="android.intent.category.LAUNCHER")的intent-filter,那么当应用安装好以后,才会在桌面创建一个图标,点击该图标打开MainActivity。现在我自定义的MyActivity 也配置了一模一样的intent-filter,那么系统也会为我的MyActivity 创建一个图标,如果用户点击了该图标,那么也可以直接打开我的MyActivity 界面。也就是说一个Android 工程可以创建多个图标,不过通常情况下一个Android 应用只要给一个入口的Activity 配置为入口Activity 即可。

 

Activity的跳转

一个Android 应用通常有多个界面,也就说有多个Activity,那么如何从一个Activity 跳转到另外一个Activity 呢?以及跳转的时候如何携带数据呢?

Activity 之间的跳转分为2 种:

 

显式跳转:在可以引用到另外一个Activity的字节码,或者包名和类名的时候,通过字节码,或者包名+类名的方法实现的跳转叫做显示跳转。显示跳转多用于自己工程内部多个Activity 之间的跳转,因为在自己工程内部可以很方便地获取到另外一个Activity 的字节码。

 

隐式跳转:隐式跳转不需要引用到另外一个Activity 的字节码,或者包名+类名,只需要知道另外一个Activity 在AndroidManifest.xml 中配置的intent-filter 中的action 和category 即可。言外之意,如果你想让你的Activity 可以被隐式意图的形式启动起来,那么就必须为该Activity 配置intent-filter。

Activity 之间的跳转都是通过Intent进行的。Intent 即意图,不仅用于描述一个Activity的信息,同时也是一个数据的载体。

开启界面的两种方式

Ø  显示意图的用法:

1.       通过Intent 的带参构造函数

Intent intent = new Intent(this, SecondActivity.class);

2.      通过Intent 的setClass 方法

intent.setClass(this,SecondActivity.class);

Intent 可以携带的数据类型

1.        八种基本数据类型boolean、byte、char、short、int、float、double、long 和String 以及这9 种数据类型的数组形式

2.        实现了Serializable 接口的对象

3.        实现了Android 的Parcelable 接口的对象以及其数组对象

 

Ø 隐式意图的用法

1.  要跳转的activity在清单文件里增在intent-filter

<intent-filter>

        <action android:name="自己定义,习惯用包名后加功能名"/>

        <categoryandroid:name="android.intent.category.DEFAULT"/> //默认

</intent-filter>

2. 谁要跳转到这个activity,谁的方法里面调用

Intentintent = new Intent();

intent.setAction("要跳转的activity在清单文件里配置的action");

intent.addCategory("android.intent.category.DEFAULT");-->默认

startActivity(intent);

Ø  隐示意图需要注意的地方

在清单文件的 intent-filter 里面还可以配置 data标签,data标签可以配置多个不同种类型的

例如:

<dataandroidscheme="自己定义"/> -->设置前缀,与电话播放器调用很像

<dataandroid:mimeType="text/plain"/> -->定义类型,这里不能随意定义

 

在java代码里,如果同时配置了scheme和mineType:

intent.setDataAndType(scheme,mimeType);

 

如果只配置scheme:

intent.setData();

 

如果只配置了mimeType:

intent.setType();

 

案例-人品计算器

综合了activity的创建,显示意图打开界面,数据的传递的一个案例。

需求分析:

主要有三个界面,分别是首页MainActivity,计算页面CalcActivity,结果界面ResultActivity。实现的效果是首页显示一个logo界面停留两秒后进入到计算页面,在计算界面输入需要测试的姓名,并勾选对应的性别点击计算按钮即可进入结果界面查看计算的结果。

具体的效果及流程如下图:

 2S后自动进入

输入姓名后计算的结果界面:

代码实现

1.       MainActivity布局:

<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"

    tools:context=".MainActivity">

 

    <ImageView

        android:layout_centerInParent="true"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:src="@drawable/logo"/>

 

</RelativeLayout>

2.       MainActivity代码自动等待两秒进入CalcActivity:

publicclass MainActivity extends Activity {

    @Override

    protectedvoid onCreate(Bundle savedInstanceState){

       super.onCreate(savedInstanceState);

       setContentView(R.layout.activity_main);

       //方式一

       new Thread(){

           publicvoid run() {

              try {

                  Thread.sleep(2000);

              } catch (InterruptedException e) {

                  e.printStackTrace();

              }

              Intent intent = new Intent();

              intent.setClass(MainActivity.this, CalcActivity.class);

              startActivity(intent);

              finish();//mainActivity关闭

           };

       }.start();

       //方式二:

       new Handler().postDelayed(new Runnable() {

           @Override

           publicvoid run() {

              Intent intent = new Intent();

              intent.setClass(MainActivity.this, CalcActivity.class);

              startActivity(intent);

              finish();//mainActivity关闭

           }

       }, 2000);

    }

}

3.  计算界面布局XML

<?xml version="1.0"encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

 

    <ImageView

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:src="@drawable/logo"/>

 

    <TextView

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="请选择用户的性别:" />

 

    <RadioGroup

        android:id="@+id/rg_sex"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">

 

        <RadioButton

            android:id="@+id/rb_male"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:checked="true"

            android:text="" />

 

        <RadioButton

            android:id="@+id/rb_female"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="" />

 

        <RadioButton

            android:id="@+id/rb_unknow"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="未知" />

    </RadioGroup>

 

    <LinearLayout

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">

 

        <EditText

            android:id="@+id/et_name"

            android:layout_width="0dip"

            android:layout_height="wrap_content"

            android:layout_weight="3"

            android:hint="请输入要计算的姓名" />

 

        <Button

            android:layout_width="0dip"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:onClick="calc"

            android:text="计算" />

    </LinearLayout>

 

</LinearLayout>

4.       计算界面代码实现:获取输入的姓名和选中的性别,传递到ResultActivity界面

publicclass CalcActivity extends Activity {

    private EditText et_name;

    private RadioGroup rg_sex;

    @Override

    protectedvoid onCreate(Bundle savedInstanceState){

       super.onCreate(savedInstanceState);

       setContentView(R.layout.activity_calc);

       et_name= (EditText) findViewById(R.id.et_name);

       rg_sex = (RadioGroup) findViewById(R.id.rg_sex);

    }

   

    publicvoid calc(View view){

       String name = et_name.getText().toString().trim();

       if(TextUtils.isEmpty(name)){

           Toast.makeText(this, "姓名不能为空", 0).show();

           return;

       }

      

       //把字符串的数据传递给第三个界面

       Intent intent = new Intent(this, ResultActivity.class);

       intent.putExtra("name", name);//在意图对象里面携带要传递的字符串数据

       intent.putExtra("sex", rg_sex.getCheckedRadioButtonId());

       intent.putExtra("bitmap", BitmapFactory.decodeResource(getResources(),R.drawable.logo));

       startActivity(intent);

    }

}

5.       ResultActivity界面布局xml:

<?xml version="1.0"encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:background="#44ff0000"

    android:gravity="center"

    android:orientation="vertical">

 

    <ImageView

        android:id="@+id/iv"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"/>

 

    <TextView

        android:id="@+id/tv_result"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="结果:"

        android:textColor="#000000"

        android:textSize="18sp"/>

 

</LinearLayout>

6.       ResultActivity具体逻辑实现

publicclass ResultActivity extends Activity {

    TextView tv_result;

 

    @Override

    protectedvoid onCreate(Bundle savedInstanceState){

       super.onCreate(savedInstanceState);

       setContentView(R.layout.activity_result);

        tv_result = (TextView) findViewById(R.id.tv_result);

       // 获取传递过来的Intent

       Intent intent = getIntent();

       // 取出对应的数据

       String name =intent.getStringExtra("name");

       Bitmap bitmap =intent.getParcelableExtra("bitmap");

       ImageView iv = (ImageView)findViewById(R.id.iv);

       iv.setImageBitmap(bitmap);

       int rb_id = intent.getIntExtra("sex", R.id.rb_male);

 

       byte[] result = null;

       try {

           switch (rb_id) {

           case R.id.rb_male:// 男性

              result =name.getBytes();

              break;

 

           case R.id.rb_female:// 女性

              result = name.getBytes("gb2312");

              break;

 

           case R.id.rb_unknow:// 未知性别:

              result = name.getBytes("iso-8859-1");

              break;

           }

       } catch (UnsupportedEncodingException e) {

           e.printStackTrace();

       }

       int total = 0;

       for (byte b : result) {

           int number = b & 0xff; // -128~127

           total += Math.abs(number);

       }

       int rp = total % 100;

       String info = null;

       if (rp > 90) {

           info = ("姓名:" + name + "\n人品为:" + rp + "\n评价:您的人品很好,祖坟冒青烟");

       } elseif (rp > 60) {

           info = ("姓名:" + name + "\n人品为:" + rp + "\n评价:您的人品还不错,继续保持");

       } elseif (rp > 30) {

           info = ("姓名:" + name + "\n人品为:" + rp + "\n评价:您的人品为渣...");

       } else {

           info = ("姓名:" + name + "\n人品为:" + rp + "\n评价:我的错,不该跟你提人品.");

       }

       tv_result.setText(info);

    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值