Android 中的图像应用程序运行缓慢

发布于 2024-11-28 17:41:03 字数 789 浏览 0 评论 0原文

我正在做一个图像应用程序以在 GridView 中显示它们,我从服务器获取 20 个图像。每个图像的分辨率是720*540。我使用JSON解析来获取url并使用下面的代码转换为Bitmap以设置图像。

public static Bitmap loadImageFromUrl(String url) {
    InputStream inputStream;Bitmap b;
    try {
        inputStream = (InputStream) new URL(url).getContent();
        BitmapFactory.Options bpo=  new BitmapFactory.Options();
        if(bpo.outWidth>500) {
            bpo.inSampleSize=8;
            b=BitmapFactory.decodeStream(inputStream, null,bpo );
        } else {
            bpo.inSampleSize=2;
            b=BitmapFactory.decodeStream(inputStream, null,bpo );
        }
        return  b;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

我的应用程序工作正常,但加载图像花费了太多时间。所以我的应用程序变得很慢。我应该降低图像的分辨率吗?

如何走出困境?

I am doing a app on images to show them in GridView, i am fetching 20 images from server. Resolution of the each image is 720*540.I used JSON parsing to fetch url and used below code to convert into Bitmap in order to set images.

public static Bitmap loadImageFromUrl(String url) {
    InputStream inputStream;Bitmap b;
    try {
        inputStream = (InputStream) new URL(url).getContent();
        BitmapFactory.Options bpo=  new BitmapFactory.Options();
        if(bpo.outWidth>500) {
            bpo.inSampleSize=8;
            b=BitmapFactory.decodeStream(inputStream, null,bpo );
        } else {
            bpo.inSampleSize=2;
            b=BitmapFactory.decodeStream(inputStream, null,bpo );
        }
        return  b;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

my app is working fine but it is taking too much time to load the images. So that my app became slow. Should i decrease the resolution of images?

how to come out of the issue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

饮惑 2024-12-05 17:41:03

如果您正在使用网格视图来加载 20 个此类分辨率的图像,我会建议您执行以下操作:

  1. 一定要减小图像的大小。除非您的目标是平板电脑,否则就没问题,因为大多数智能手机无法通过 20 张图像达到该分辨率。

  2. 如果可以的话缓存图像。

  3. 在不同的线程上下载图像。存储一个 HashMap 会让你变得很容易,只需将所有图像视图以图像文件名或其他形式的 ID 作为键即可。下载图像时向您的处理程序发送消息,并在解码后更新视图。您可以直接检索您的视图。请记住检查它们是否仍在窗口中。这样,图像就会快速地一个接一个地显示出来。我不认为多线程处理图像会有帮助,只需确保使用另一个线程“推送图像”和主 UI 线程更新即可。这样用户体验就会大大提高。

希望这有帮助。

---一些实现,我现在没有完整的代码---

有一个数据结构来将视图与传入的数据相匹配。这里非常方便。

private HashMap<String,ImageView> pictures;

当您获取图像 url 列表时,迭代它们:(

 pictures.put(id,view);
        try{
            FileInputStream in = openFileInput(id);
            Bitmap bitmap = null;
            bitmap = BitmapFactory.decodeStream(in, null, null);
        view.setImageBitmap(bitmap);
        }catch(Exception e){
            new Thread(new PictureGetter(this,mHandler,id)).start();
        }

这里图片获取器将简单地获取图像(如果尚未缓存)并缓存它)

更新图像视图的代码:

 if(id!=null){
        ImageView iv = pictures.get(id);
        if(iv!=null){
            try{
                FileInputStream in = openFileInput(id);
                Bitmap bitmap = null;
                bitmap = BitmapFactory.decodeStream(in, null, null);
                iv.setImageBitmap(bitmap);
            }catch(Exception e){
        }
    }

If you are doing a grid view to load 20 images of such resolution, I would suggest the following:

  1. Definitely reduce the size of the images. Unless you are targeting a tablet, you will be fine as most smartphones cannot achieve that resolution with 20 images.

  2. Cache images if you can.

  3. Download the images on a different thread. Store a HashMap would make it easy for you, just put all the imageviews with the image file names or other form of IDs as keys. send message to your Handler when images are downloaded and update the view after it's decoded. You can retrieve your views directly. Just remember to check if they are still in the window. This way the images will show up one after another quickly. I don't think multithreading the images will help, just make sure to use another thread to "push the images" and the main UI thread updates. User experience will be greatly improved then.

Hope this helps.

---some implementations, I don't have the complete code with me right now---

Have a data structure to match the views with data that comes in. very handy here.

private HashMap<String,ImageView> pictures;

When you get the list of image urls, iterate through them:

 pictures.put(id,view);
        try{
            FileInputStream in = openFileInput(id);
            Bitmap bitmap = null;
            bitmap = BitmapFactory.decodeStream(in, null, null);
        view.setImageBitmap(bitmap);
        }catch(Exception e){
            new Thread(new PictureGetter(this,mHandler,id)).start();
        }

(Here the picture getter will simply fetch the image if it is not cached already and cache it)

Code to update the image view:

 if(id!=null){
        ImageView iv = pictures.get(id);
        if(iv!=null){
            try{
                FileInputStream in = openFileInput(id);
                Bitmap bitmap = null;
                bitmap = BitmapFactory.decodeStream(in, null, null);
                iv.setImageBitmap(bitmap);
            }catch(Exception e){
        }
    }
会傲 2024-12-05 17:41:03

Picasso 库

解决方案是不使用位图直接加载图像,而是使用一个名为 Picasso 的很棒的库,它的速度超级快我知道你真的很喜欢这个,你可以像这样

将 picasso jar 文件添加到你的项目中(在此处下载 picasso jar 文件)使用 picasso 加载图像,如下所示

Picasso.with(context).load(new File(title)).centerCrop()
    .resize(150, 150).error(R.drawable.ic_launcher).into(image);  

,其中标题是你要加载的图像路径。裁剪、调整大小、错误是可选的。

Picasso library

Solution is instead of using bitmap to load image directly use a awesome Library called Picasso its just super fast i know you really love this you can do this like this

Add picasso jar file to your project (Download picasso jar file here) Use picasso to load the Image like this

Picasso.with(context).load(new File(title)).centerCrop()
    .resize(150, 150).error(R.drawable.ic_launcher).into(image);  

where title is the image path which you want to load. Crop,resize, error are optional.

世界等同你 2024-12-05 17:41:03

我猜测大部分加载时间是因为大量图像加上图像大小。

有 2 种可能的解决方案:

  1. 调整图像大小,或降低图像质量,使文件大小低于 75kb 左右。

  2. 使用多线程一次检索多个图像。如果用户的连接速度真的很慢,这可能没有帮助,但如果您将其与足够小的文件大小结合起来,它可能会有所帮助。您可能想要确定设备的当前带宽是多少,并根据在该带宽上运行的线程数确定。

例如:20 个图像(每个图像 75KB)和 200 KB/s 的可用连接 = 3 或 4 个并发线程。

希望这有帮助。

I'm guessing that most of the loading time is because of the large amount of images combined with the size of the images.

There are 2 possible solutions:

  1. Resize the images, or lower the quality of the images so that the filesize is below 75kb or so.

  2. Use multi-threading to retrieve multiple images at once. This might not help if the user's connection is really slow, but if you combine this with a small enough filesize it might just help out enough. You might want to determine what the current bandwidth of the device is and base the number of threads you run on that.

For instance: 20 images of 75KB each and an available connection of 200 KB/s = 3 or 4 concurrent threads.

Hope this helps.

橘寄 2024-12-05 17:41:03

我的 Android 应用程序也有同样的问题。当您从大尺寸图像中解码位图并将其设置为图像视图的 imageBitmap 时,您的应用程序可能会变慢,经过几次尝试后,您将收到“内存不足异常”

您可以尝试处理此问题的两种可能方法问题:
1-从文件解码时减小位图大小
2- 使用图像库。

我更喜欢第二种方式并使用通用图像加载器。 https://github.com/nostra13/Android-Universal-Image-Loader

String url = "file://" + your_file_path
com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(url, ivPicture, options);

I have same problem in my android app. When you decode a bitmap from a big sized image and set as imageBitmap to an image view probably your application will slow and after a few try you'll get an "out of memory exception"

Two of the possible ways you can try to handle this problem:
1- Reduce bitmap size when you decode from file
2- Use an image library.

I prefered second way and used Universal Image Loader. https://github.com/nostra13/Android-Universal-Image-Loader

String url = "file://" + your_file_path
com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(url, ivPicture, options);
没有你我更好 2024-12-05 17:41:03
 public class clothImageLoader {

// the simplest in-memory cache implementation. This should be replaced with
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
// public static HashMap<String, Bitmap> cache = new HashMap<String,
// Bitmap>();

private static File cacheDir;

public clothImageLoader(Context context) {
    // Make the background thead low priority. This way it will not affect
    // the UI performance
    photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);

    // Find the dir to save cached images
    if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
        // cacheDir=new
        // File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
        cacheDir = new File(ConstValue.MY_ClothBitmap_DIR);
    else
        cacheDir = context.getCacheDir();
    if (!cacheDir.exists())
        cacheDir.mkdirs();
}

final int stub_id = R.drawable.icon;

public void DisplayImage(String url, Activity activity, ImageView imageView) {
    if (ConstValue.ClothRoomcache.containsKey(url))
        imageView.setImageBitmap(ConstValue.ClothRoomcache.get(url));
    else {
        queuePhoto(url, activity, imageView);
        imageView.setImageResource(stub_id);
    }
}

private void queuePhoto(String url, Activity activity, ImageView imageView) {
    // This ImageView may be used for other images before. So there may be
    // some old tasks in the queue. We need to discard them.
    photosQueue.Clean(imageView);
    PhotoToLoad p = new PhotoToLoad(url, imageView);
    synchronized (photosQueue.photosToLoad) {
        photosQueue.photosToLoad.push(p);
        photosQueue.photosToLoad.notifyAll();
    }

    // start thread if it's not started yet
    if (photoLoaderThread.getState() == Thread.State.NEW)
        photoLoaderThread.start();
}

private Bitmap getBitmap(String url) {
    // I identify images by hashcode. Not a perfect solution, good for the
    // demo.
    String filename = String.valueOf(url.hashCode());
    File f = new File(cacheDir, filename);

    // from SD cache
    Bitmap b = decodeFile(f);
    if (b != null)
        return b;

    // from web
    try {
        Bitmap bitmap = null;
        /*
         * InputStream is=new URL(url).openStream(); OutputStream os = new
         * FileOutputStream(f); Utils.CopyStream(is, os); os.close();
         */
        URL url1 = new URL(url);
        bitmap = decodeFile(f);
        /* Open a connection to that URL. */
        URLConnection ucon = url1.openConnection();

        /*
         * Define InputStreams to read from the URLConnection.
         */
        InputStream is = ucon.getInputStream();
        // FlushedInputStream a = new FlushedInputStream(is);
        BufferedInputStream bis = new BufferedInputStream(is);

        /*
         * Read bytes to the Buffer until there is nothing more to read(-1).
         */
        ByteArrayBuffer baf = new ByteArrayBuffer(5000);
        int current = 0;
        while ((current = bis.read()) != -1) {
            baf.append((byte) current);
        }

        /* Convert the Bytes read to a String. */
        FileOutputStream fos = new FileOutputStream(f);
        fos.write(baf.toByteArray());
        fos.flush();
        fos.close();

        bitmap = decodeFile(f);
        return bitmap;
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;
    }
}

// decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);
        // Find the correct scale value. It should be the power of 2.
        final int REQUIRED_SIZE = ConstValue.bmpSize;
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE)
                break;
            width_tmp /= 2;
            height_tmp /= 2;
            scale++;
        }

        // decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {
    }
    return null;
}

// Task for the queue
private class PhotoToLoad {
    public String url;
    public ImageView imageView;

    public PhotoToLoad(String u, ImageView i) {
        url = u;
        imageView = i;
    }
}

PhotosQueue photosQueue = new PhotosQueue();

public void stopThread() {
    photoLoaderThread.interrupt();
}

// stores list of photos to download
class PhotosQueue {
    private Stack<PhotoToLoad> photosToLoad = new Stack<PhotoToLoad>();

    // removes all instances of this ImageView
    public void Clean(ImageView image) {
        for (int j = 0; j < photosToLoad.size();) {
            if (photosToLoad.get(j).imageView == image)
                photosToLoad.remove(j);
            else
                ++j;
        }
    }
}

class PhotosLoader extends Thread {
    public void run() {
        try {
            while (true) {
                // thread waits until there are any images to load in the
                // queue
                if (photosQueue.photosToLoad.size() == 0)
                    synchronized (photosQueue.photosToLoad) {
                        photosQueue.photosToLoad.wait();
                    }
                if (photosQueue.photosToLoad.size() != 0) {
                    PhotoToLoad photoToLoad;
                    synchronized (photosQueue.photosToLoad) {
                        photoToLoad = photosQueue.photosToLoad.pop();

                        // photoToLoad=photosQueue.photosToLoad.get(0);
                        // photosQueue.photosToLoad.remove(photoToLoad);
                    }
                    Bitmap bmp = getBitmap(photoToLoad.url);
                    ConstValue.ClothRoomcache.put(photoToLoad.url, bmp);
                    if (((String) photoToLoad.imageView.getTag()).equals(photoToLoad.url)) {
                        BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad.imageView);
                        Activity a = (Activity) photoToLoad.imageView.getContext();
                        a.runOnUiThread(bd);
                    }
                }
                if (Thread.interrupted())
                    break;
            }
        } catch (InterruptedException e) {
            // allow thread to exit
        }
    }
}

PhotosLoader photoLoaderThread = new PhotosLoader();

// Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable {
    Bitmap bitmap;
    ImageView imageView;

    public BitmapDisplayer(Bitmap b, ImageView i) {
        bitmap = b;
        imageView = i;
    }

    public void run() {
        if (bitmap != null)
            imageView.setImageBitmap(bitmap);
        else
            imageView.setImageResource(stub_id);
    }
}

public static void clearCache() {
    // clear memory cache
    ConstValue.ClothRoomcache.clear();

    // clear SD cache
    File[] files = cacheDir.listFiles();
    for (File f : files)
        f.delete();
}

public class FlushedInputStream extends FilterInputStream {
    public FlushedInputStream(InputStream inputStream) {
        super(inputStream);
    }

    @Override
    public long skip(long n) throws IOException {
        long totalBytesSkipped = 0L;
        while (totalBytesSkipped < n) {
            long bytesSkipped = in.skip(n - totalBytesSkipped);
            if (bytesSkipped == 0L) {
                int a = read();
                if (a < 0) {
                    break; // we reached EOF
                } else {
                    bytesSkipped = 1; // we read one byte
                }
            }
            totalBytesSkipped += bytesSkipped;
        }
        return totalBytesSkipped;
    }
}

}

当您调用该方法时,在 gridView getView 方法中:

holder.image.setTag(ChoseInfo.get(position).getLink());
        imageLoader.DisplayImage(ChoseInfo.get(position).getLink(), activity, holder.image);

选择Info.get(position).getLink())

这里getLink()是互联网链接。

 public class clothImageLoader {

// the simplest in-memory cache implementation. This should be replaced with
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
// public static HashMap<String, Bitmap> cache = new HashMap<String,
// Bitmap>();

private static File cacheDir;

public clothImageLoader(Context context) {
    // Make the background thead low priority. This way it will not affect
    // the UI performance
    photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);

    // Find the dir to save cached images
    if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
        // cacheDir=new
        // File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
        cacheDir = new File(ConstValue.MY_ClothBitmap_DIR);
    else
        cacheDir = context.getCacheDir();
    if (!cacheDir.exists())
        cacheDir.mkdirs();
}

final int stub_id = R.drawable.icon;

public void DisplayImage(String url, Activity activity, ImageView imageView) {
    if (ConstValue.ClothRoomcache.containsKey(url))
        imageView.setImageBitmap(ConstValue.ClothRoomcache.get(url));
    else {
        queuePhoto(url, activity, imageView);
        imageView.setImageResource(stub_id);
    }
}

private void queuePhoto(String url, Activity activity, ImageView imageView) {
    // This ImageView may be used for other images before. So there may be
    // some old tasks in the queue. We need to discard them.
    photosQueue.Clean(imageView);
    PhotoToLoad p = new PhotoToLoad(url, imageView);
    synchronized (photosQueue.photosToLoad) {
        photosQueue.photosToLoad.push(p);
        photosQueue.photosToLoad.notifyAll();
    }

    // start thread if it's not started yet
    if (photoLoaderThread.getState() == Thread.State.NEW)
        photoLoaderThread.start();
}

private Bitmap getBitmap(String url) {
    // I identify images by hashcode. Not a perfect solution, good for the
    // demo.
    String filename = String.valueOf(url.hashCode());
    File f = new File(cacheDir, filename);

    // from SD cache
    Bitmap b = decodeFile(f);
    if (b != null)
        return b;

    // from web
    try {
        Bitmap bitmap = null;
        /*
         * InputStream is=new URL(url).openStream(); OutputStream os = new
         * FileOutputStream(f); Utils.CopyStream(is, os); os.close();
         */
        URL url1 = new URL(url);
        bitmap = decodeFile(f);
        /* Open a connection to that URL. */
        URLConnection ucon = url1.openConnection();

        /*
         * Define InputStreams to read from the URLConnection.
         */
        InputStream is = ucon.getInputStream();
        // FlushedInputStream a = new FlushedInputStream(is);
        BufferedInputStream bis = new BufferedInputStream(is);

        /*
         * Read bytes to the Buffer until there is nothing more to read(-1).
         */
        ByteArrayBuffer baf = new ByteArrayBuffer(5000);
        int current = 0;
        while ((current = bis.read()) != -1) {
            baf.append((byte) current);
        }

        /* Convert the Bytes read to a String. */
        FileOutputStream fos = new FileOutputStream(f);
        fos.write(baf.toByteArray());
        fos.flush();
        fos.close();

        bitmap = decodeFile(f);
        return bitmap;
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;
    }
}

// decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);
        // Find the correct scale value. It should be the power of 2.
        final int REQUIRED_SIZE = ConstValue.bmpSize;
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE)
                break;
            width_tmp /= 2;
            height_tmp /= 2;
            scale++;
        }

        // decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {
    }
    return null;
}

// Task for the queue
private class PhotoToLoad {
    public String url;
    public ImageView imageView;

    public PhotoToLoad(String u, ImageView i) {
        url = u;
        imageView = i;
    }
}

PhotosQueue photosQueue = new PhotosQueue();

public void stopThread() {
    photoLoaderThread.interrupt();
}

// stores list of photos to download
class PhotosQueue {
    private Stack<PhotoToLoad> photosToLoad = new Stack<PhotoToLoad>();

    // removes all instances of this ImageView
    public void Clean(ImageView image) {
        for (int j = 0; j < photosToLoad.size();) {
            if (photosToLoad.get(j).imageView == image)
                photosToLoad.remove(j);
            else
                ++j;
        }
    }
}

class PhotosLoader extends Thread {
    public void run() {
        try {
            while (true) {
                // thread waits until there are any images to load in the
                // queue
                if (photosQueue.photosToLoad.size() == 0)
                    synchronized (photosQueue.photosToLoad) {
                        photosQueue.photosToLoad.wait();
                    }
                if (photosQueue.photosToLoad.size() != 0) {
                    PhotoToLoad photoToLoad;
                    synchronized (photosQueue.photosToLoad) {
                        photoToLoad = photosQueue.photosToLoad.pop();

                        // photoToLoad=photosQueue.photosToLoad.get(0);
                        // photosQueue.photosToLoad.remove(photoToLoad);
                    }
                    Bitmap bmp = getBitmap(photoToLoad.url);
                    ConstValue.ClothRoomcache.put(photoToLoad.url, bmp);
                    if (((String) photoToLoad.imageView.getTag()).equals(photoToLoad.url)) {
                        BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad.imageView);
                        Activity a = (Activity) photoToLoad.imageView.getContext();
                        a.runOnUiThread(bd);
                    }
                }
                if (Thread.interrupted())
                    break;
            }
        } catch (InterruptedException e) {
            // allow thread to exit
        }
    }
}

PhotosLoader photoLoaderThread = new PhotosLoader();

// Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable {
    Bitmap bitmap;
    ImageView imageView;

    public BitmapDisplayer(Bitmap b, ImageView i) {
        bitmap = b;
        imageView = i;
    }

    public void run() {
        if (bitmap != null)
            imageView.setImageBitmap(bitmap);
        else
            imageView.setImageResource(stub_id);
    }
}

public static void clearCache() {
    // clear memory cache
    ConstValue.ClothRoomcache.clear();

    // clear SD cache
    File[] files = cacheDir.listFiles();
    for (File f : files)
        f.delete();
}

public class FlushedInputStream extends FilterInputStream {
    public FlushedInputStream(InputStream inputStream) {
        super(inputStream);
    }

    @Override
    public long skip(long n) throws IOException {
        long totalBytesSkipped = 0L;
        while (totalBytesSkipped < n) {
            long bytesSkipped = in.skip(n - totalBytesSkipped);
            if (bytesSkipped == 0L) {
                int a = read();
                if (a < 0) {
                    break; // we reached EOF
                } else {
                    bytesSkipped = 1; // we read one byte
                }
            }
            totalBytesSkipped += bytesSkipped;
        }
        return totalBytesSkipped;
    }
}

}

when you call the method ,in the gridView getView method:

holder.image.setTag(ChoseInfo.get(position).getLink());
        imageLoader.DisplayImage(ChoseInfo.get(position).getLink(), activity, holder.image);

ChoseInfo.get(position).getLink())

Here getLink() is internet link.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文