160

私が作成している Android アプリケーションの Web サイトに HTTP 取得要求を行っています。

DefaultHttpClient を使用し、HttpGet を使用してリクエストを発行しています。エンティティの応答を取得し、これからページの html を取得するための InputStream オブジェクトを取得します。

次に、次のように返信を繰り返します。

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String x = "";
x = r.readLine();
String total = "";

while(x!= null){
total += x;
x = r.readLine();
}

ただし、これは恐ろしく遅いです。

これは非効率ですか?大きな Web ページ ( www.cokezone.co.uk)を読み込んでいないので、ファイル サイズは大きくありません。これを行うより良い方法はありますか?

ありがとう

アンディ

4

12 に答える 12

362

コードの問題は、大量の重いStringオブジェクトを作成し、その内容をコピーして操作を実行していることです。代わりに、追加ごとにStringBuilder新しいオブジェクトを作成しないようにし、char 配列をコピーしないようにするために を使用する必要があります。Stringあなたの場合の実装は次のようになります。

BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
    total.append(line).append('\n');
}

totalに変換せずに使用できるようStringになりましたが、結果が として必要な場合は、次Stringを追加するだけです。

文字列の結果 = total.toString();

私はそれをよりよく説明しようとします...

  • a += b(またはa = a + b)、ここでaとは文字列であり、との両方bの内容を新しいオブジェクトにコピーし (蓄積された を含むもコピーしていることに注意してください)、各反復でこれらのコピーを実行しています。 a ba String
  • a.append(b)、ここaで、はコンテンツをStringBuilderに直接追加するため、反復ごとに蓄積された文字列をコピーしません。ba
于 2010-03-30T22:42:01.597 に答える
35

ストリームを文字列に変換する組み込みメソッドを試しましたか? これは、Apache Commons ライブラリ (org.apache.commons.io.IOUtils) の一部です。

次に、コードは次の1行になります。

String total = IOUtils.toString(inputStream);

ドキュメントはここにあります: http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html#toString%28java.io.InputStream%29

Apache Commons IO ライブラリは、http: //commons.apache.org/io/download_io.cgiからダウンロードできます。

于 2010-08-24T00:27:13.893 に答える
11

これで十分効率的だと思います... InputStream から文字列を取得するには、次のメソッドを呼び出します。

public static String getStringFromInputStream(InputStream stream) throws IOException
{
    int n = 0;
    char[] buffer = new char[1024 * 4];
    InputStreamReader reader = new InputStreamReader(stream, "UTF8");
    StringWriter writer = new StringWriter();
    while (-1 != (n = reader.read(buffer))) writer.write(buffer, 0, n);
    return writer.toString();
}

私は常にUTF-8を使用しています。もちろん、InputStream の他に、charset を引数として設定することもできます。

于 2014-08-16T20:21:09.937 に答える
7

これはどうですか。より良いパフォーマンスを与えるようです。

byte[] bytes = new byte[1000];

StringBuilder x = new StringBuilder();

int numRead = 0;
while ((numRead = is.read(bytes)) >= 0) {
    x.append(new String(bytes, 0, numRead));
}

編集:実際には、この種はsteelbytesとMauricePerryの両方を含みます

于 2010-03-24T16:04:31.727 に答える
4

おそらくハイメ・ソリアーノの答えよりもいくらか速く、エイドリアンの答えのマルチバイト・エンコーディングの問題がなければ、私は提案します:

File file = new File("/tmp/myfile");
try {
    FileInputStream stream = new FileInputStream(file);

    int count;
    byte[] buffer = new byte[1024];
    ByteArrayOutputStream byteStream =
        new ByteArrayOutputStream(stream.available());

    while (true) {
        count = stream.read(buffer);
        if (count <= 0)
            break;
        byteStream.write(buffer, 0, count);
    }

    String string = byteStream.toString();
    System.out.format("%d bytes: \"%s\"%n", string.length(), string);
} catch (IOException e) {
    e.printStackTrace();
}
于 2015-03-21T22:47:28.520 に答える
3

「一度に 1 行ずつ」読み取って文字列を結合するのではなく、「使用可能なすべてを読み取る」ことを試して、行末のスキャンを回避し、文字列の結合も回避します。

すなわち、InputStream.available()そしてInputStream.read(byte[] b), int offset, int length)

于 2010-03-24T00:46:43.453 に答える
2

一度に 1 行のテキストを読み取り、その行を文字列に個別に追加するのは、各行の抽出と非常に多くのメソッド呼び出しのオーバーヘッドの両方に時間がかかります。

適切なサイズのバイト配列を割り当ててストリーム データを保持し、必要に応じてより大きな配列に繰り返し置き換え、配列が保持できる限り多くを読み取ろうとすることで、パフォーマンスを向上させることができました。

なんらかの理由で、コードが HTTPUrlConnection によって返された InputStream を使用すると、Android はファイル全体のダウンロードに繰り返し失敗しました。そのため、ファイル全体を取得するかキャンセルするかを確実にするために、BufferedReader と手動のタイムアウト メカニズムの両方を使用する必要がありました。転送。

private static  final   int         kBufferExpansionSize        = 32 * 1024;
private static  final   int         kBufferInitialSize          = kBufferExpansionSize;
private static  final   int         kMillisecondsFactor         = 1000;
private static  final   int         kNetworkActionPeriod        = 12 * kMillisecondsFactor;

private String loadContentsOfReader(Reader aReader)
{
    BufferedReader  br = null;
    char[]          array = new char[kBufferInitialSize];
    int             bytesRead;
    int             totalLength = 0;
    String          resourceContent = "";
    long            stopTime;
    long            nowTime;

    try
    {
        br = new BufferedReader(aReader);

        nowTime = System.nanoTime();
        stopTime = nowTime + ((long)kNetworkActionPeriod * kMillisecondsFactor * kMillisecondsFactor);
        while(((bytesRead = br.read(array, totalLength, array.length - totalLength)) != -1)
        && (nowTime < stopTime))
        {
            totalLength += bytesRead;
            if(totalLength == array.length)
                array = Arrays.copyOf(array, array.length + kBufferExpansionSize);
            nowTime = System.nanoTime();
        }

        if(bytesRead == -1)
            resourceContent = new String(array, 0, totalLength);
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }

    try
    {
        if(br != null)
            br.close();
    }
    catch(IOException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

編集:コンテンツを再エンコードする必要がない場合 (つまり、コンテンツをAS ISにしたい場合) は、 Reader サブクラスを使用しないでください。適切な Stream サブクラスを使用するだけです。

前のメソッドの先頭を次の対応する行に置き換えて、さらに 2 ~ 3 倍高速化します。

String  loadContentsFromStream(Stream aStream)
{
    BufferedInputStream br = null;
    byte[]              array;
    int                 bytesRead;
    int                 totalLength = 0;
    String              resourceContent;
    long                stopTime;
    long                nowTime;

    resourceContent = "";
    try
    {
        br = new BufferedInputStream(aStream);
        array = new byte[kBufferInitialSize];
于 2014-01-13T07:10:13.063 に答える
1

ファイルが長い場合は、各行に String 連結を使用する代わりに、StringBuilder に追加することでコードを最適化できます。

于 2010-03-22T12:54:37.110 に答える
-2

私は完全なデータを読むのに慣れています:

// inputStream is one instance InputStream
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
String dataString = new String(data);

これはディスクに保存されたファイルに適用され、デフォルト サイズのないストリームには適用されないことに注意してください。

于 2015-12-31T15:30:22.460 に答える